{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": [],
      "collapsed_sections": [],
      "toc_visible": true
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    },
    "gpuClass": "standard",
    "accelerator": "GPU"
  },
  "cells": [
    {
      "cell_type": "markdown",
      "source": [
        "# StableDiffusionGenerator へようこそ！"
      ],
      "metadata": {
        "id": "JBF83zzBX0bX"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "# 概要\n",
        "\n",
        "画像生成AI [Stable Diffusion](https://github.com/CompVis/stable-diffusion) に日本語文章で指示が出せるColabページです。\n",
        "\n",
        " - [pypi](https://pypi.org/project/diffusers/)\n",
        " - [Github](https://github.com/huggingface/diffusers/)\n",
        "\n",
        "また、[FILM](https://github.com/google-research/frame-interpolation) を使って画像間の補間アニメーションも出力出来ます。"
      ],
      "metadata": {
        "id": "y91R-eenYL8a"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "## 始めに\n",
        "\n",
        "このツールの稼働状況やメンテナンス情報はTwitter（[@miu200521358](https://twitter.com/miu200521358)）にて行っています。\n",
        "\n",
        "エラーになる、起動しない、などの場合、まずは現在の配布状況をご確認ください。\n",
        "\n",
        "リプやDM等でのお問い合わせも受け付けています。\n",
        "\n",
        "また、StableDiffusionではトークンの取得のため、会員登録をお願いしています。\n",
        "\n",
        "ノートブックの使い方にはクセがあるので、慣れてない方は「[MMD自動トレース 準備編](https://colab.research.google.com/github/miu200521358/motion_trace_colab/blob/master/AutoTraceIntroduction.ipynb)」で使い方を確認してみてください。\n",
        "\n",
        "（MMD自動トレース用とは銘打ってますが、Colabの使い方のご案内だけなのでご安心ください）"
      ],
      "metadata": {
        "id": "G_NHoKDOGQuh"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "## 目次\n",
        "\n",
        "\n",
        "画面左上の「＝」（実際は三本線）をクリックして下さい。目次が開きます。（既に開いている場合は次へ進んでください）\n",
        "\n",
        "![目次](https://drive.google.com/uc?export=view&id=1HGk4sJmcPtMbMwcJOvE3aU1GjvKinwA_)\n",
        "\n",
        "ノートブックを上から順に確認し、以下手順をひとつずつ実行してください。\n",
        "\n",
        "実行が必要なセルには番号を振ってあります。①から順番に実行してください。\n",
        "\n",
        "- **「①　トークンの取得」**\n",
        "  - Stable Diffusion の生成に必要な会員登録、トークンの取得、配置を行います。\n",
        "- **「②　環境構築」**\n",
        "  - Stable Diffusion 用の環境を構築します。\n",
        "- **「③　Stable Diffusion 実行」**\n",
        "    - Stable Diffusion　を実行します。"
      ],
      "metadata": {
        "id": "RlfAse_pZe38"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "# ①　事前準備"
      ],
      "metadata": {
        "id": "7X3KzECRGfgB"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "## ①-A トークンの取得\n",
        "\n",
        "Stable Diffusion を実行するために必要な会員登録・トークンの取得・保存を行います。\n",
        "\n",
        "2回目以降で既にGoogleドライブの「StableDiffusionGenerator」フォルダに「token.txt」ファイルが保存出来ている場合、このセルはスキップして大丈夫です。\n",
        "\n",
        " 1. [Hugging Face](https://huggingface.co/) を開きます。\n",
        "\n",
        " 2. 右上の「Sing Up」ボタンをクリックします\n",
        " \n",
        " ![SingUp](https://drive.google.com/uc?export=view&id=1LJ9f7gNKdvzNKd0d7YdkRtNWYa0hB-Ix)\n",
        "\n",
        " 3. メールアドレスとパスワードを入力して、「Next」ボタンをクリックします\n",
        "\n",
        " ![Next](https://drive.google.com/uc?export=view&id=1g-5noep5GDuevftp9CjcGE-kH4JE2VxJ)\n",
        "\n",
        " 4. ユーザーアカウントを作成します\n",
        "  - プロフィールを入力します\n",
        "      - 表示名とフルネームは必須で、表示名は既にある名前は使用できません\n",
        "      - 名前は本名でなくても大丈夫です\n",
        "  - 規約を確認のチェックボックスをONにします\n",
        "  - 「Create」ボタンをクリックします\n",
        "\n",
        " ![Create](https://drive.google.com/uc?export=view&id=1cCwKc6gT0olpak5sEz99l68LCGgp3KBJ)\n",
        "\n",
        " 5. 登録したメールアドレス宛に確認メールが届いていますので、認証します\n",
        "   - 認証しないとTokenが作れません\n",
        "\n",
        " ![Confirm](https://drive.google.com/uc?export=view&id=12q93P7CdExx-JzgQ4pkcyywJze6kGfHc)\n",
        "\n",
        " 6. サイト右上のアイコンから「Settings」をクリックします\n",
        "\n",
        " ![Setting](https://drive.google.com/uc?export=view&id=13IAc-OADAsjjucc2L9YB0qtC6HF1nULF)\n",
        "\n",
        " 7. 設定画面から「Access Tokens」をクリックします\n",
        "\n",
        "![Token](https://drive.google.com/uc?export=view&id=12RWD4KuoSLvLnfmlDhjmsox1Eo3HR8m0)\n",
        "\n",
        " 8. トークン画面から「New Token」をクリックします\n",
        "\n",
        " ![New Token](https://drive.google.com/uc?export=view&id=1dXM75ebaSTdksQ3VH5RV5NYF9EX804LL)\n",
        "\n",
        " 9. ダイアログが出てきますので、作成します\n",
        "  - Name: StableDiffusionGenerator\n",
        "  - Role: read（読み取り専用）\n",
        "  - 「Generate a token」をクリックします\n",
        "\n",
        " ![Generate](https://drive.google.com/uc?export=view&id=15F4LQ48BU6osUMzxhuxi5vOzCea_I8CR)\n",
        "\n",
        " 10. Tokenが出来たら、Googleドライブを新しく開きます\n",
        "  - **Tokenは後でコピーするので、ページは閉じないでください！**\n",
        "\n",
        " 11. GoogleドライブのマイドライブTOPページで、StableDiffusionGenerator用フォルダを作成します\n",
        "  - フォルダ名: StableDiffusionGenerator\n",
        "  - 手打ちだとスペルミスなどが起こりやすいので、コピペしてください\n",
        "\n",
        "![DriveTop](https://drive.google.com/uc?export=view&id=1S2CPelTc2sLtaNgl9uBjvBeNCweG5Gz5)\n",
        "\n",
        "![NewFolder](https://drive.google.com/uc?export=view&id=1gigxWvtF0sGYkw7-P1uRGc2YfbJ9FBH_)\n",
        "\n",
        "![FolderCreate](https://drive.google.com/uc?export=view&id=1lrXAxKcWncyQAwR1f1UAU2wfe_uwpJ6H)\n",
        "\n",
        " 12. GoogleドライブにTextEditorが入ってない場合、インストールします\n",
        "\n",
        " - 新規作成 ＞　その他　＞　アプリを追加\n",
        "\n",
        "![AddApp](https://drive.google.com/uc?export=view&id=1qXBgNuTsb5iXI63RTjYu9204FZaKap62)\n",
        "\n",
        " - `Text Editor` を入力し `デベロッパー:Text Editor for Google Drive` 製のTextEditorをインストールする\n",
        "\n",
        " ![Select](https://drive.google.com/uc?export=view&id=1Xl7QaG7ofibjNj5ZLP5n_DTJ-adEC89_)\n",
        "\n",
        "![Install](https://drive.google.com/uc?export=view&id=1YAWqkgo7D9dwGXW7Ufx0cFdsP158jQu6)\n",
        "\n",
        " 13. 「StableDiffusionGenerator」フォルダ内にToken用テキストファイルを作成します\n",
        "\n",
        "![GCreate](https://drive.google.com/uc?export=view&id=1mZGVamD2kkAcrfmAJQ4k6V3uD9x37v84)\n",
        "\n",
        "![TextFile](https://drive.google.com/uc?export=view&id=1gceum1CWOSSnwaafeRxcXefuxf3vUYGX)\n",
        "\n",
        "![NewText](https://drive.google.com/uc?export=view&id=1EKotPDJaDimmvall1NoUl38qfnija1Kg)\n",
        "\n",
        " 14. テキスト編集画面が開けたら、ファイル名を「token.txt」に変更します\n",
        "\n",
        "![Title](https://drive.google.com/uc?export=view&id=1dQDPpFhEZrj6fnx07_R4Zdog8Pe-h1z0)\n",
        "\n",
        "![TitleChange](https://drive.google.com/uc?export=view&id=1etXecganDzvRMv01DIhoNOvY-gM3ZStw)\n",
        "\n",
        " 15. 「Hugging Face」のToken画面に戻って、Token欄の右横にあるコピーボタンをクリックします\n",
        "\n",
        "![Copy](https://drive.google.com/uc?export=view&id=1h83fIEInPEoEF9P9CtXV7OW70Ps2b3qP)\n",
        "\n",
        " 16. Googleドライブのテキスト編集画面に戻って、Tokenを貼り付けます\n",
        "  - 貼り付けるのは、「Ctrl+V」や「右クリック」などで出来ます\n",
        "\n",
        "![Paste](https://drive.google.com/uc?export=view&id=1GoSqQk6mdjO0Oizn1HCUIQ3Ec58RPBBI)\n",
        "\n",
        " 17. 「token.txt」をGoogleドライブの「StableDiffusionGenerator」に保存します\n",
        " - 初めての場合、アクセス許可確認が出てくるので、許可してください\n",
        "\n",
        "![FileSave](https://drive.google.com/uc?export=view&id=18ntx-ViSPAWNvi7y4OwlXxmzzXElFAqy)\n",
        "\n",
        " ![Account](https://drive.google.com/uc?export=view&id=1Fcu1U2M1_S9_rhQ_ss_FUDRD6CPg_-uQ)\n",
        "\n",
        "![Allow](https://drive.google.com/uc?export=view&id=1HuhVzk7rZAYEuh1eVRb5WrScLRj9haPz)\n",
        "\n",
        " 18. テキスト編集画面に「Saved」と表示され、Googleドライブの「StableDiffusionGenerator」フォルダ内に「token.txt」が出来ていたら成功です\n",
        "\n",
        "![Saved](https://drive.google.com/uc?export=view&id=1BLKHtVlVlP6d_r36w7jVDIDht-ojuUfY)\n",
        "\n",
        "![DriveText](https://drive.google.com/uc?export=view&id=1CcCSW4JZQVVEq7LVZRlQye1bx2ggJBMN)"
      ],
      "metadata": {
        "id": "IFiUrE6obDl7"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "## ①-B 学習モデルへのアクセス許可\n",
        "\n",
        "Stable Diffusion を実行するために必要な学習モデルへのアクセスを許可します。\n",
        "\n",
        " 1. [学習モデルページ](https://huggingface.co/CompVis/stable-diffusion-v1-4) を開きます。\n",
        "\n",
        "![Model](https://drive.google.com/uc?export=view&id=1LD_ZIIUWjLIp766yn4ARVOEfohjryH5A)\n",
        "\n",
        " 2. 画面中央にあるライセンス確認チェックボックスをONにして、「Access Repository」ボタンをクリックします。\n",
        " \n",
        "![Licence](https://drive.google.com/uc?export=view&id=1riAaBua6nrF53d_8w5rmMFfTkqEnbq0U)"
      ],
      "metadata": {
        "id": "TAZIfbab6RJ8"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "# ②　環境構築"
      ],
      "metadata": {
        "id": "2izP4YvYq_WH"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "## ②-A　通知音のダウンロード\n",
        "\n",
        "通知音を[効果音ラボ](https://soundeffect-lab.info/)様よりダウンロードして、セルの実行を開始・完了・失敗した時に音が鳴るようにします。\n",
        "\n",
        "下の【②-A】のセルを実行してください\n",
        "\n"
      ],
      "metadata": {
        "id": "bdYvMFG0tpa6"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "#@markdown ■■■■■■■■■■■■■■■■■■\n",
        "\n",
        "#@markdown 【②-A】　通知音のダウンロード\n",
        "\n",
        "#@markdown ■■■■■■■■■■■■■■■■■■\n",
        "\n",
        "from enum import Enum\n",
        "\n",
        "class SoundType(Enum):\n",
        "    START = 0\n",
        "    SUCCESS = 1\n",
        "    FAIL = 2\n",
        "\n",
        "# ----------------------------------\n",
        "\n",
        "! wget --no-check-certificate -c \"https://soundeffect-lab.info/sound/anime/mp3/sceneswitch1.mp3\"\n",
        "! wget --no-check-certificate -c \"https://soundeffect-lab.info/sound/anime/mp3/incorrect1.mp3\"\n",
        "! wget --no-check-certificate -c \"https://soundeffect-lab.info/sound/anime/mp3/switch1.mp3\"\n",
        "\n",
        "from IPython.display import Audio, display\n",
        "import torch\n",
        "\n",
        "def play_sound(sound_type: SoundType, autoplay=True):\n",
        "    try:\n",
        "        if sound_type == SoundType.START:\n",
        "            file_name = \"/content/switch1.mp3\"\n",
        "        elif sound_type == SoundType.SUCCESS:\n",
        "            file_name = \"/content/sceneswitch1.mp3\"\n",
        "        else:\n",
        "            file_name = \"/content/incorrect1.mp3\"\n",
        "\n",
        "        display(Audio(file_name, autoplay=autoplay, normalize=False))\n",
        "    except:\n",
        "        print(\"■■■■■■■■■■■■■■■\")\n",
        "        print(\"■　効果音が再生できませんでした\")\n",
        "        print(\"■■■■■■■■■■■■■■■\")\n",
        "\n",
        "# ----------------------------------\n",
        "\n",
        "exec_dict = {\n",
        "    '②-A': {'exec': False, 'premise': []},\n",
        "    '②-B': {'exec': False, 'premise': ['②-A', ]},\n",
        "    '②-C': {'exec': False, 'premise': ['②-A', '②-B', ]},\n",
        "    '②-D': {'exec': False, 'premise': ['②-A', '②-B', '②-C', ]},\n",
        "    '②-E': {'exec': False, 'premise': ['②-A', '②-B', '②-C', '②-D', ]},\n",
        "    '③-A': {'exec': False, 'premise': ['②-A', '②-B', '②-C', '②-D', '②-E', ]},\n",
        "    '③-B': {'exec': False, 'premise': ['②-A', '②-B', '②-C', '②-D', '②-E', '③-A', ]},\n",
        "    '④-A': {'exec': False, 'premise': ['②-A', '②-B', '②-C', '②-D', '②-E', ]},\n",
        "    '④-B': {'exec': False, 'premise': ['②-A', '②-B', '②-C', '②-D', '②-E', '④-A', ]},\n",
        "    '⑤-A': {'exec': False, 'premise': ['②-A', '②-B', '②-C', '②-D', '②-E', ]},\n",
        "    '⑤-B': {'exec': False, 'premise': ['②-A', '②-B', '②-C', '②-D', '②-E', '⑤-A', ]},\n",
        "    '⑥-A': {'exec': False, 'premise': ['②-A', '②-B', '②-C', '②-D', '②-E', ]},\n",
        "    '⑥-B': {'exec': False, 'premise': ['②-A', '②-B', '②-C', '②-D', '②-E', '⑥-A', ]},\n",
        "}\n",
        "\n",
        "NVIDIA_VISIBLE_DEVICES=all\n",
        "\n",
        "class IpyExit(SystemExit):\n",
        "    def __init__(self):\n",
        "        play_sound(SoundType.FAIL)\n",
        "        pass\n",
        "\n",
        "def check_exec_dict(cell_key: str):\n",
        "    if not torch.cuda.is_available():\n",
        "        print(\"■■■■■■■■■■■■■■■\")\n",
        "        print(\"■　** ERROR **\")\n",
        "        print(\"■　GPUが有効になってません。\")\n",
        "        print(\"■　準備編を参考にしながらランタイムをGPUに変更してください。\")\n",
        "        print(\"■■■■■■■■■■■■■■■\")\n",
        "        raise IpyExit\n",
        "\n",
        "    if not exec_dict:\n",
        "        print(\"■■■■■■■■■■■■■■■\")\n",
        "        print(\"■　** ERROR **\")\n",
        "        print(\"■　セル【②-A】が実行されていない可能性があります。\")\n",
        "        print(\"■　目次から戻って実行してください。\")\n",
        "        print(\"■■■■■■■■■■■■■■■\")\n",
        "        raise IpyExit\n",
        "\n",
        "    for p in exec_dict[cell_key]['premise']:\n",
        "        # 事前に実行されるべきセルがすべて実行されているか\n",
        "        if not exec_dict[p]['exec']:\n",
        "            print(\"■■■■■■■■■■■■■■■\")\n",
        "            print(\"■　** ERROR **\")\n",
        "            print(f\"■　セル【{p}】が実行されていない可能性があります。\")\n",
        "            print(\"■　目次から戻って実行してください。\")\n",
        "            print(\"■■■■■■■■■■■■■■■\")\n",
        "            raise IpyExit\n",
        "\n",
        "    # 全部実行されていたらOK\n",
        "    play_sound(SoundType.START)\n",
        "    return True\n",
        "\n",
        "def finish_cell(cell_key: str):\n",
        "    play_sound(SoundType.SUCCESS)\n",
        "\n",
        "    print(\"■■■■■■■■■■■■■■■\")\n",
        "    print(\"■　** OK **\")\n",
        "    print(f\"■　セル【{cell_key}】の実行に成功しました。\")\n",
        "    print(\"■■■■■■■■■■■■■■■\")\n",
        "\n",
        "    exec_dict[cell_key]['exec'] = True\n",
        "\n",
        "# ----------------------------------\n",
        "\n",
        "# セル実行可否\n",
        "ckey = \"②-A\"\n",
        "check_exec_dict(ckey)\n",
        "\n",
        "# ----------------------------------\n",
        "\n",
        "# セル終了\n",
        "\n",
        "finish_cell(ckey)\n",
        "\n",
        "print(\"\")\n",
        "print(\"\")\n",
        "print(\"■ セル実行開始\")\n",
        "play_sound(SoundType.START, autoplay=False)\n",
        "\n",
        "print(\"\")\n",
        "print(\"\")\n",
        "print(\"■ セル実行失敗\")\n",
        "play_sound(SoundType.FAIL, autoplay=False)\n",
        "\n",
        "print(\"\")\n",
        "print(\"\")\n",
        "print(\"■ セル実行成功\")\n",
        "play_sound(SoundType.SUCCESS, autoplay=False)"
      ],
      "metadata": {
        "cellView": "form",
        "id": "uRhGeU8HuK7D"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## ②-B　GPUランタイムの確認\n",
        "\n",
        "ヘッダの \"ランタイム\"　＞　\"ランタイムのタイプを変更\"　＞　\"GPU\"　を選択して下さい。\n",
        "\n",
        "詳しい手順は、「[MMD自動トレース 準備編](https://colab.research.google.com/github/miu200521358/motion_trace_colab/blob/master/AutoTraceIntroduction.ipynb)」を参考にしてください。\n",
        "\n",
        "変更できたら、下の【②-B】のセルを実行して下さい。"
      ],
      "metadata": {
        "id": "Gs63hvYzyQ02"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "#@markdown ■■■■■■■■■■■■■■■■■■\n",
        "\n",
        "#@markdown 【②-B】　GPUランタイムの確認\n",
        "\n",
        "#@markdown ■■■■■■■■■■■■■■■■■■\n",
        "\n",
        "# セル実行可否\n",
        "ckey = \"②-B\"\n",
        "check_exec_dict(ckey)\n",
        "\n",
        "# ----------------------------------\n",
        "! nvidia-smi\n",
        "\n",
        "import subprocess\n",
        "try:\n",
        "    subprocess.check_output(\"nvidia-smi\", shell=True)\n",
        "except:\n",
        "    print(\"■■■■■■■■■■■■■■■\")\n",
        "    print(\"■　** ERROR **\")\n",
        "    print(\"■　nvidia-smiコマンドの実行に失敗しました。\")\n",
        "    print(\"■　① まだGPUを使ってない場合、ランタイムの設定を行っていない場合\")\n",
        "    print(\"■　　準備編を参考にしながら、ランタイムをGPUに設定してください\")\n",
        "    print(\"■　　※ランタイムを変更すると環境がリセットされるため、【②-A】からやり直してください\")\n",
        "    print(\"■　② GPUの利用上限に達して、CPUでしか使えない場合\")\n",
        "    print(\"■　　無料域の場合、24時間以上置いていただくと再度使用できるようになります\")\n",
        "    print(\"■■■■■■■■■■■■■■■\")\n",
        "\n",
        "# ----------------------------------\n",
        "\n",
        "# セル終了\n",
        "finish_cell(ckey)"
      ],
      "metadata": {
        "id": "1Blo2wb4ynKu",
        "cellView": "form"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## ②-C　Googleドライブとの連携\n",
        "\n",
        "先ほどGoogleドライブに保存した「token.txt」を読み取る為、このColabページからあなたのGoogleドライブにアクセスする許可を与えてください。\n",
        "\n",
        "※Googleドライブ内の「StableDiffusionGenerator」フォルダ以外は触りません\n",
        "\n",
        "下の【②-C】のセルを実行して、連携許可のキーを入力欄に「Ctrl+V」で貼り付けて下さい。"
      ],
      "metadata": {
        "id": "lai2gccsrDhM"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "#@markdown ■■■■■■■■■■■■■■■■■■\n",
        "\n",
        "#@markdown 【②-C】　Googleドライブとの連携\n",
        "\n",
        "#@markdown ■■■■■■■■■■■■■■■■■■\n",
        "\n",
        "# セル実行可否\n",
        "ckey = \"②-C\"\n",
        "check_exec_dict(ckey)\n",
        "\n",
        "# ----------------------------------\n",
        "\n",
        "from google.colab import drive\n",
        "import os\n",
        "\n",
        "# Googleドライブマウント\n",
        "drive.mount('/gdrive')\n",
        "\n",
        "# 起点ディレクトリ\n",
        "base_dir_path = \"/gdrive/My Drive/StableDiffusionGenerator\"\n",
        "token_path = f\"{base_dir_path}/token.txt\"\n",
        "\n",
        "if os.path.exists(base_dir_path):\n",
        "    if os.path.exists(token_path):\n",
        "        print(\"■■■■■■■■■■■■■■■\")\n",
        "        print(\"■　** OK **\")\n",
        "        print(\"■　StableDiffusionGeneratorフォルダ内との連携が成功し、token.txtを確認しました。\")\n",
        "        print(\"■■■■■■■■■■■■■■■\")\n",
        "    else:\n",
        "        print(\"■■■■■■■■■■■■■■■\")\n",
        "        print(\"■　** ERROR **\")\n",
        "        print(\"■　StableDiffusionGeneratorフォルダ内にtoken.txtが見つかりませんでした。\")\n",
        "        print(\"■　①-A を参考にしながら、token.txtを作成してください\")\n",
        "        print(\"■■■■■■■■■■■■■■■\")\n",
        "        raise IpyExit\n",
        "else:\n",
        "    print(\"■■■■■■■■■■■■■■■\")\n",
        "    print(\"■　** ERROR **\")\n",
        "    print(\"■　StableDiffusionGeneratorフォルダがGoogleドライブの直下に見つかりませんでした。\")\n",
        "    print(\"■　①-A を参考にしながら、StableDiffusionGeneratorフォルダとtoken.txtを作成してください。\")\n",
        "    print(\"■　※スペルミスだった場合、ファイル名などの変更は行わず、新しいフォルダもしくはファイルで試してください。\")\n",
        "    print(\"■　　Googleドライブとの連携後の更新はうまく認識されません。\")\n",
        "    print(\"■　※少しタイムラグがあるので、修正した直後は認識されない場合があります。\")\n",
        "    print(\"■　　10秒くらい待ってからセルを再実行してください。\")\n",
        "    print(\"■■■■■■■■■■■■■■■\")\n",
        "    raise IpyExit\n",
        "\n",
        "# ----------------------------------\n",
        "\n",
        "# セル終了\n",
        "finish_cell(ckey)"
      ],
      "metadata": {
        "cellView": "form",
        "id": "rnNbVjnrrmYG"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## ②-D　コードセットアップ\n",
        "\n",
        "StableDiffusion と FILM のコードを構築します。\n",
        "\n",
        "下の【②-D】のセルを実行して下さい。"
      ],
      "metadata": {
        "id": "83BACNkQ1duS"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "#@markdown ■■■■■■■■■■■■■■■■■■\n",
        "\n",
        "#@markdown 【②-D】　コードセットアップ\n",
        "\n",
        "#@markdown ■■■■■■■■■■■■■■■■■■\n",
        "\n",
        "# セル実行可否\n",
        "ckey = \"②-D\"\n",
        "check_exec_dict(ckey)\n",
        "\n",
        "# ----------------------------------\n",
        "\n",
        "# Stable Diffusion\n",
        "!pip install diffusers==0.3.0 transformers scipy ftfy\n",
        "\n",
        "# ----------------------------------\n",
        "\n",
        "# FILM\n",
        "! git clone https://github.com/google-research/frame-interpolation frame_interpolation\n",
        "\n",
        "# コードセットアップ\n",
        "# Colab 用に全部手動で入れ直す\n",
        "! pip install tensorflow==2.8.0 # The latest should include tensorflow-gpu\n",
        "! pip install tensorflow-datasets==4.4.0\n",
        "! pip install tensorflow-addons==0.15.0\n",
        "! pip install apache-beam==2.34.0\n",
        "! pip install mediapy==1.0.3\n",
        "! pip install loguru\n",
        "! pip install absl-py==0.12.0\n",
        "! pip install gin-config==0.5.0\n",
        "! pip install parameterized==0.8.1\n",
        "! pip install mediapy==1.0.3\n",
        "! pip install scikit-image==0.19.1\n",
        "! pip install apache-beam==2.34.0\n",
        "! pip install google-cloud-bigquery-storage==1.1.0 # Suppresses a harmless error from beam\n",
        "! pip install natsort==8.1.0\n",
        "! pip install ipywidgets==7.7.1\n",
        "\n",
        "import sys\n",
        "sys.path.append(\"/content/frame_interpolation\")\n",
        "\n",
        "# ----------------------------------\n",
        "\n",
        "text2img_pipes = {True: None, False: None}\n",
        "img2img_pipe = None\n",
        "inpainting_pipe = None\n",
        "\n",
        "# セル終了\n",
        "finish_cell(ckey)"
      ],
      "metadata": {
        "cellView": "form",
        "id": "BnBh48so1rt-"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## ②-E　Tokenの認証と学習モデルの選択\n",
        "\n",
        "StableDiffusionでTokenを使った認証を行い、必要な学習モデルを選択します。\n",
        "\n",
        "お好きな学習モデルを選択して、【②-E】のセルを実行して下さい。\n",
        "\n",
        "※学習モデルのダウンロードは各処理の初回実行時に行います"
      ],
      "metadata": {
        "id": "dWIXrS0A5BsF"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "#@markdown ■■■■■■■■■■■■■■■■■■\n",
        "\n",
        "#@markdown 【②-E】　Tokenの認証と学習モデルのダウンロード\n",
        "\n",
        "#@markdown ■■■■■■■■■■■■■■■■■■\n",
        "\n",
        "#@markdown 学習モデル\n",
        "difussion_model = \"CompVis/stable-diffusion-v1-4\"  #@param ['CompVis/stable-diffusion-v1-4', 'hakurei/waifu-diffusion']\n",
        "\n",
        "#@markdown  - [CompVis/stable-diffusion-v1-4](https://huggingface.co/CompVis/stable-diffusion-v1-4): デフォルトの Stable Diffusion モデル\n",
        "\n",
        "#@markdown  - [hakurei/waifu-diffusion](https://huggingface.co/hakurei/waifu-diffusion) : 「[Danbooru](https://danbooru.donmai.us)」チューニングアニメ・マンガ特化モデル\n",
        "\n",
        "# - [naclbit/trinart_stable_diffusion](https://huggingface.co/naclbit/trinart_stable_diffusion) : 「[とりんさまAI](https://twitter.com/trinsama)」チューニングアニメ・マンガ特化モデル\n",
        "\n",
        "trinart = difussion_model == \"naclbit/trinart_stable_diffusion\"\n",
        "\n",
        "# セル実行可否\n",
        "ckey = \"②-E\"\n",
        "check_exec_dict(ckey)\n",
        "\n",
        "# ----------------------------------\n",
        "\n",
        "# トークン読み込み\n",
        "with open(token_path, 'r') as f:\n",
        "    access_tokens = f.read()\n",
        "import torch\n",
        "from diffusers import StableDiffusionPipeline\n",
        "from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_img2img import StableDiffusionImg2ImgPipeline, preprocess\n",
        "from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_inpaint import StableDiffusionInpaintPipeline, preprocess_image, preprocess_mask\n",
        "from PIL import Image\n",
        "import traceback\n",
        "\n",
        "# ----------------------------------\n",
        "\n",
        "# モデルロード\n",
        "torch_device = \"cuda\"\n",
        "\n",
        "\n",
        "def init_text2img_pipe():\n",
        "    try:\n",
        "        global text2img_pipes\n",
        "        # print(f\"T: {text2img_pipes[True]}, F: {text2img_pipes[False]}\")\n",
        "        if text2img_pipes[True] and text2img_pipes[False]:\n",
        "          return\n",
        "    except Exception as e:\n",
        "        print(e)\n",
        "        print(traceback.format_exc())\n",
        "        text2img_pipes = {True: None, False: None}\n",
        "\n",
        "    print(\"■■■■■■■■■■■■■■■\")\n",
        "    print(\"■　text2image用パイプライン生成開始\")\n",
        "    print(\"■■■■■■■■■■■■■■■\")\n",
        "\n",
        "    conv2d_init = torch.nn.Conv2d.__init__\n",
        "    convtranspose2d_init = torch.nn.ConvTranspose2d.__init__\n",
        "\n",
        "    def init_conv2d_circular(self, *args, **kwargs):\n",
        "        return conv2d_init(self, *args, **kwargs, padding_mode='circular')\n",
        "\n",
        "    def init_conv2d_zeros(self, *args, **kwargs):\n",
        "        return conv2d_init(self, *args, **kwargs, padding_mode='zeros')\n",
        "\n",
        "    def init_convtranspose2d_circular(self, *args, **kwargs):\n",
        "        return convtranspose2d_init(self, *args, **kwargs, padding_mode='circular')\n",
        "\n",
        "    def init_convtranspose2d_zeros(self, *args, **kwargs):\n",
        "        return convtranspose2d_init(self, *args, **kwargs, padding_mode='zeros')\n",
        "\n",
        "    try:\n",
        "        # シームレス用と通常用のパイプラインを生成する\n",
        "        for b in [True, False]:\n",
        "            if b:\n",
        "              torch.nn.Conv2d.__init__ = init_conv2d_circular\n",
        "              torch.nn.ConvTranspose2d.__init__ = init_convtranspose2d_circular\n",
        "            else:\n",
        "              torch.nn.Conv2d.__init__ = init_conv2d_zeros\n",
        "              torch.nn.ConvTranspose2d.__init__ = init_convtranspose2d_zeros\n",
        "\n",
        "            # パイプライン生成\n",
        "            if trinart:\n",
        "                text2img_pipes[b] = StableDiffusionPipeline.from_pretrained(difussion_model, use_auth_token=access_tokens)  \n",
        "            else:\n",
        "                text2img_pipes[b] = StableDiffusionPipeline.from_pretrained(difussion_model, revision=\"fp16\", torch_dtype=torch.float16, use_auth_token=access_tokens)  \n",
        "            if b:\n",
        "                print(\"■■■■■■■■■■■■■■■\")\n",
        "                print(\"■　text2image（シームレス）用パイプライン生成成功\")\n",
        "                print(\"■■■■■■■■■■■■■■■\")\n",
        "            else:\n",
        "                print(\"■■■■■■■■■■■■■■■\")\n",
        "                print(\"■　text2image用パイプライン生成成功\")\n",
        "                print(\"■■■■■■■■■■■■■■■\")\n",
        "    except Exception as e:\n",
        "        print(e)\n",
        "        print(traceback.format_exc())\n",
        "\n",
        "        print(\"■■■■■■■■■■■■■■■\")\n",
        "        print(\"■　** ERROR **\")\n",
        "        print(\"■　学習モデルへのアクセスもしくはセットアップに失敗しました。\")\n",
        "        print(\"■　　①-B を参考にしながら、学習モデルへのアクセスを許可してください。\")\n",
        "        print(\"■　　重複して実行すると、学習モデルのセットアップ処理に失敗する場合があります。\")\n",
        "        print(\"■　　失敗した場合、「ヘッダ＞ランタイム＞セッションの管理」からセッションを一度削除してください。\")\n",
        "        print(\"■■■■■■■■■■■■■■■\")\n",
        "        raise IpyExit\n",
        "\n",
        "def init_img2img_pipe():\n",
        "    try:\n",
        "        global img2img_pipe\n",
        "        # print(f\"img2img_pipe: {img2img_pipe}\")\n",
        "        if img2img_pipe:\n",
        "            return\n",
        "    except Exception as e:\n",
        "        print(e)\n",
        "        print(traceback.format_exc())\n",
        "        img2img_pipe = None\n",
        "\n",
        "    print(\"■■■■■■■■■■■■■■■\")\n",
        "    print(\"■　img2img用パイプライン生成開始\")\n",
        "    print(\"■■■■■■■■■■■■■■■\")\n",
        "\n",
        "    try:\n",
        "        # img2img用パイプライン生成\n",
        "        if trinart:\n",
        "            img2img_pipe = StableDiffusionImg2ImgPipeline.from_pretrained(difussion_model, use_auth_token=access_tokens)  \n",
        "        else:\n",
        "            img2img_pipe = StableDiffusionImg2ImgPipeline.from_pretrained(difussion_model, revision=\"fp16\", torch_dtype=torch.float16, use_auth_token=access_tokens)  \n",
        "\n",
        "        print(\"■■■■■■■■■■■■■■■\")\n",
        "        print(\"■　img2img用パイプライン生成成功\")\n",
        "        print(\"■■■■■■■■■■■■■■■\")\n",
        "    except Exception as e:\n",
        "        print(e)\n",
        "        print(traceback.format_exc())\n",
        "\n",
        "        print(\"■■■■■■■■■■■■■■■\")\n",
        "        print(\"■　** ERROR **\")\n",
        "        print(\"■　img2img用パイプラインのセットアップに失敗しました。\")\n",
        "        print(\"■　　重複して実行すると、パイプラインのセットアップ処理に失敗する場合があります。\")\n",
        "        print(\"■■■■■■■■■■■■■■■\")\n",
        "        raise IpyExit\n",
        "\n",
        "    return img2img_pipe\n",
        "\n",
        "def init_inpainting_pipe():\n",
        "    try:\n",
        "        global inpainting_pipe\n",
        "        # print(f\"inpainting_pipe: {inpainting_pipe}\")\n",
        "        if inpainting_pipe:\n",
        "            return\n",
        "    except Exception as e:\n",
        "        print(e)\n",
        "        print(traceback.format_exc())\n",
        "        inpainting_pipe = None\n",
        "\n",
        "    print(\"■■■■■■■■■■■■■■■\")\n",
        "    print(\"■　inpainting用パイプライン生成開始\")\n",
        "    print(\"■■■■■■■■■■■■■■■\")\n",
        "\n",
        "    try:\n",
        "        # inpainting用パイプライン生成\n",
        "        if trinart:\n",
        "            inpainting_pipe = StableDiffusionInpaintPipeline.from_pretrained(difussion_model, use_auth_token=access_tokens)  \n",
        "        else:\n",
        "            inpainting_pipe = StableDiffusionInpaintPipeline.from_pretrained(difussion_model, revision=\"fp16\", torch_dtype=torch.float16, use_auth_token=access_tokens)  \n",
        "\n",
        "        print(\"■■■■■■■■■■■■■■■\")\n",
        "        print(\"■　inpainting用パイプライン生成成功\")\n",
        "        print(\"■■■■■■■■■■■■■■■\")\n",
        "    except Exception as e:\n",
        "        print(e)\n",
        "        print(traceback.format_exc())\n",
        "\n",
        "        print(\"■■■■■■■■■■■■■■■\")\n",
        "        print(\"■　** ERROR **\")\n",
        "        print(\"■　inpainting用パイプラインのセットアップに失敗しました。\")\n",
        "        print(\"■　　重複して実行すると、パイプラインのセットアップ処理に失敗する場合があります。\")\n",
        "        print(\"■■■■■■■■■■■■■■■\")\n",
        "        raise IpyExit\n",
        "\n",
        "    return inpainting_pipe\n",
        "\n",
        "# ----------------------------------\n",
        "\n",
        "import requests\n",
        "import json\n",
        "\n",
        "def translate_jp2en(japanese_text: str):\n",
        "    if not japanese_text:\n",
        "      # テキスト入力がない場合、スルー\n",
        "      return \"\"\n",
        "\n",
        "    # 値がないメッセージを翻訳\n",
        "    params = (\n",
        "        (\"text\", japanese_text),\n",
        "        (\"source\", \"ja\"),\n",
        "        (\"target\", \"en\"),\n",
        "    )\n",
        "\n",
        "    # GASを叩く\n",
        "    # https://qiita.com/satto_sann/items/be4177360a0bc3691fdf\n",
        "    response = requests.get(\n",
        "        \"https://script.google.com/macros/s/AKfycbzZtvOvf14TaMdRIYzocRcf3mktzGgXvlFvyczo/exec\",\n",
        "        params=params,\n",
        "    )\n",
        "\n",
        "    # 結果を解析\n",
        "    results = json.loads(response.text)\n",
        "\n",
        "    if \"text\" in results:\n",
        "        return results[\"text\"]\n",
        "    else:\n",
        "        print(\"■■■■■■■■■■■■■■■\")\n",
        "        print(\"■　** ERROR **\")\n",
        "        print(\"■　日本語から英語への翻訳に失敗しました。\")\n",
        "        print(\"■　少し時間をおいて再度試してみてください。\")\n",
        "        print(\"■■■■■■■■■■■■■■■\")\n",
        "        raise IpyExit\n",
        "\n",
        "# ----------------------------------\n",
        "\n",
        "import os\n",
        "from datetime import datetime\n",
        "import torch\n",
        "from torch import autocast\n",
        "import ipywidgets as widgets\n",
        "from ipywidgets import IntSlider, FloatSlider, Label, Layout, HBox, VBox, GridBox, RadioButtons, Text\n",
        "from IPython.display import display\n",
        "import functools\n",
        "import random\n",
        "from PIL import Image\n",
        "from tqdm import tqdm\n",
        "\n",
        "def create_prompt(*args, **kwargs):\n",
        "    japanese_prompt = kwargs.get(\"japanese_prompt\", \"\")\n",
        "    world = kwargs.get(\"world\", \"\")\n",
        "    image_type = kwargs.get(\"image_type\", \"\")\n",
        "    technique = kwargs.get(\"technique\", \"\")\n",
        "    supply = kwargs.get(\"supply\", \"\")\n",
        "    school = kwargs.get(\"school\", \"\")\n",
        "    photo = kwargs.get(\"photo\", \"\")\n",
        "    perspective = kwargs.get(\"perspective\", \"\")\n",
        "    camera = kwargs.get(\"camera\", \"\")\n",
        "    length = kwargs.get(\"length\", \"\")\n",
        "    blur = kwargs.get(\"blur\", \"\")\n",
        "    tone = kwargs.get(\"tone\", \"\")\n",
        "    hue = kwargs.get(\"hue\", \"\")\n",
        "    author = kwargs.get(\"author\", \"\")\n",
        "    english_prompt = kwargs.get(\"english_prompt\", \"\")\n",
        "    seamless = kwargs.get(\"seamless\", False)\n",
        "    detail = kwargs.get(\"detail\", False)\n",
        "    face = kwargs.get(\"face\", False)\n",
        "    upper = kwargs.get(\"upper\", False)\n",
        "    lower = kwargs.get(\"lower\", False)\n",
        "    leg = kwargs.get(\"leg\", False)\n",
        "    illust = kwargs.get(\"illust\", False)\n",
        "    aspect = kwargs.get(\"aspect\", \"1:1\")\n",
        "    width = kwargs.get(\"width\", 320)\n",
        "    image_nums = kwargs.get(\"image_nums\", 3)\n",
        "    num_inference_steps = kwargs.get(\"num_inference_steps\", 50)\n",
        "    guidance_scale = kwargs.get(\"guidance_scale\", 7.5)\n",
        "    manual_seed = kwargs.get(\"manual_seed\", 0)\n",
        "    \n",
        "\n",
        "    # 画像サイズは必須\n",
        "    aspect_x, aspect_y = aspect.split(\":\")\n",
        "    height = int(int(width) / int(aspect_x) * int(aspect_y))\n",
        "    jp_prompts = []\n",
        "    en_prompts = []\n",
        "    detail_prompts = []\n",
        "    human_prompts = []\n",
        "\n",
        "    # 日本語Promptは全角空白で区切って翻訳して設定\n",
        "    jp_prompts.extend(japanese_prompt.split(\"　\"))\n",
        "\n",
        "    # 英語Promptはそのまま設定\n",
        "    en_prompts.append(english_prompt)\n",
        "\n",
        "    for target, suffix in [(world, \"\"), (image_type, \"\"), (author, \"が作った\"), (supply, \"で描いた\"), (technique, \"\"), \n",
        "                           (perspective, \"\"), (photo, \"\"), (camera, \"\"), (length, \"\"), (blur, \"\"), (tone, \"\"), (hue, \"の色味が強い\"), (school, \"\")]:\n",
        "        if target and len(target.split(':')):\n",
        "            # プロンプトが指定されているものだけ追加\n",
        "            jp_prompts.append(f\"{target.split(':')[0]}{suffix}\")\n",
        "\n",
        "    if face or upper or lower:\n",
        "        human_prompts.extend([\"human\"])\n",
        "\n",
        "    if face:\n",
        "        human_prompts.extend([\"face\", \"head\", \"hair\", \"forehead\", \"temple\", \"face\", \"eye\", \"eyebrow\", \"eyelash\", \"eyelid\", \"eyeball\", \"iris\", \"cornea\", \"nose\", \"cheek\", \"mouth\", \"lips\", \"ear\", \"jaw\", \"neck\"])\n",
        "\n",
        "    if upper:\n",
        "        human_prompts.extend([\"upper\", \"hand\", \"palm\", \"finger\", \"arm\", \"elbow\", \"wrist\", \"shoulder\", \"chest\", \"finger\"])\n",
        "\n",
        "    if lower:\n",
        "        human_prompts.extend([\"lower\", \"waist\", \"back\", \"leg\", \"knee\", \"hip\"])\n",
        "\n",
        "    if leg:\n",
        "        human_prompts.extend([\"ankle\", \"toe\", \"heel\"])\n",
        "\n",
        "    if illust:\n",
        "        human_prompts.extend([ \"kawaii\", \"illust\", \"emotional\", \"pixiv\"])\n",
        "\n",
        "    if detail:\n",
        "        detail_prompts.append(\"high detail\")\n",
        "        detail_prompts.append(\"high quarity\")\n",
        "\n",
        "    # 翻訳したのをリストアップ\n",
        "    prompts = []\n",
        "    other_promts = []\n",
        "    prompts.append(\", \".join([f\"( {translate_jp2en(p)} )\" for p in jp_prompts]))\n",
        "    if en_prompts and len(en_prompts[0]):\n",
        "      prompts.append(\"(\" + \" \".join(en_prompts) + \")\")\n",
        "      other_promts.append(\"(\" + \" \".join(en_prompts) + \")\")\n",
        "    if human_prompts and len(human_prompts[0]):\n",
        "      prompts.append(\"(\" + \" \".join(human_prompts) + \")\")\n",
        "      other_promts.append(\"(\" + \" \".join(human_prompts) + \")\")\n",
        "    if detail_prompts and len(detail_prompts[0]):\n",
        "      prompts.append(\"(\" + \", \".join(detail_prompts) + \")\")\n",
        "      other_promts.append(\"(\" + \" \".join(detail_prompts) + \")\")\n",
        "\n",
        "    # シードは0の場合はランダムとする\n",
        "    seed = manual_seed if manual_seed else random.randint(1, 1024)\n",
        "\n",
        "    return \", \".join(prompts), seed, jp_prompts, other_promts, int(width), height\n",
        "\n",
        "# ------------------------\n",
        "def execute(\n",
        "    prompt,\n",
        "    jp_prompt,\n",
        "    seamless,\n",
        "    seed,\n",
        "    aspect,\n",
        "    width,\n",
        "    height,\n",
        "    image_cnts,\n",
        "    n_steps,\n",
        "    g_scale,\n",
        "    saving,\n",
        "    img2img=False,\n",
        "    inpainting=False,\n",
        "    init_image_path=None,\n",
        "    mask_image_path=None,\n",
        "    strength=0\n",
        "):\n",
        "    # 指示日時\n",
        "    now_str = f\"{datetime.now():%Y%m%d_%H%M%S}\"\n",
        "\n",
        "    if width % 64 or height % 64:\n",
        "        print(\"■■■■■■■■■■■■■■■\")\n",
        "        print(\"■　** ERROR **\")\n",
        "        print(\"■　アスペクト比に基づいた画像サイズの縦横が64で割り切れない値になっています。\")\n",
        "        print(\"■　アスペクト比もしくは、画像横幅の指定を見直してください。\")\n",
        "        print(\n",
        "            f\"■　アスペクト比： {aspect}, 画像横幅: {width}({width / 64:.3f}), 画像縦幅: {height}({height / 64:.3f})\"\n",
        "        )\n",
        "        print(\"■■■■■■■■■■■■■■■\")\n",
        "        raise IpyExit\n",
        "\n",
        "    # テクスチャフラグで切替\n",
        "    generator = torch.Generator(torch_device).manual_seed(seed)\n",
        "    file_paths = []\n",
        "\n",
        "    print(\"■■■■■■■■■■■■■■■\")\n",
        "    print(\"■　** PROMPT **\")\n",
        "    print(f\"■　{prompt}\")\n",
        "    print(\"■　** SEED **\")\n",
        "    print(f\"■　{seed}\")\n",
        "    print(\"■■■■■■■■■■■■■■■\")\n",
        "\n",
        "    # 初期画像\n",
        "    init_image = None\n",
        "    mask_image = None\n",
        "    if img2img:\n",
        "        im = Image.open(init_image_path)\n",
        "        im = im.convert('RGB')\n",
        "        init_image = preprocess(im)\n",
        "        init_img2img_pipe()\n",
        "    elif inpainting:\n",
        "        im = Image.open(init_image_path)\n",
        "        init_image = im.convert('RGB')\n",
        "\n",
        "        im = Image.open(mask_image_path)\n",
        "        mask_image = im.convert('RGB')\n",
        "\n",
        "        init_inpainting_pipe()\n",
        "    else:\n",
        "        init_text2img_pipe()\n",
        "\n",
        "    output_dir = \"/content/outputs\"\n",
        "\n",
        "    if img2img:\n",
        "        output_dir = f\"/content/outputs/{now_str}\"\n",
        "\n",
        "    # 出力用フォルダ生成\n",
        "    os.makedirs(output_dir, exist_ok=True)\n",
        "\n",
        "    # 指示文を出力\n",
        "    with open(\n",
        "        f\"{output_dir}/StableDiffusionGenerator_{now_str}.txt\", \"w\", encoding=\"UTF-8\"\n",
        "    ) as f:\n",
        "        f.write(jp_prompt)\n",
        "        f.write(\"\\n\")\n",
        "        f.write(prompt)\n",
        "        f.write(\"\\n\")\n",
        "        f.write(f\"{seed}\")\n",
        "\n",
        "    # 画像を生成\n",
        "    with autocast(torch_device):\n",
        "        for n in range(image_cnts):\n",
        "            m = 0\n",
        "            try:\n",
        "                # 一括で指定すると負荷が高くなるので繰り返す\n",
        "                if img2img:\n",
        "                    img2img_pipe.to(torch_device)\n",
        "                    # NSFWの制限を外す\n",
        "                    img2img_pipe.safety_checker = lambda images, **kwargs: (images, False)\n",
        "                    # strength が 0の場合、経過で変化させる\n",
        "                    sg = strength if strength else (min(1, (1 / image_cnts) * (n + 1)))\n",
        "                    print(f\"strength: {sg}\")\n",
        "                    image = img2img_pipe(\n",
        "                        prompt=prompt,\n",
        "                        init_image=init_image,\n",
        "                        strength=sg,\n",
        "                        guidance_scale=g_scale,\n",
        "                        num_inference_steps=n_steps,\n",
        "                    )[\"sample\"][0]\n",
        "                elif inpainting:\n",
        "                    inpainting_pipe.to(torch_device)\n",
        "                    # NSFWの制限を外す\n",
        "                    inpainting_pipe.safety_checker = lambda images, **kwargs: (images, False)\n",
        "                    # strength が 0の場合、経過で変化させる\n",
        "                    sg = strength if strength else (min(1, (1 / image_cnts) * (n + 1)))\n",
        "                    print(f\"strength: {sg}\")\n",
        "                    image = inpainting_pipe(\n",
        "                        prompt=prompt,\n",
        "                        init_image=init_image,\n",
        "                        mask_image=mask_image,\n",
        "                        strength=sg,\n",
        "                        guidance_scale=g_scale,\n",
        "                        num_inference_steps=n_steps,\n",
        "                    )[\"sample\"][0]\n",
        "                else:\n",
        "                    # text2img\n",
        "                    text2img_pipes[seamless].to(torch_device)\n",
        "                    if saving:\n",
        "                        # 省エネモード\n",
        "                        text2img_pipes[seamless].enable_attention_slicing()\n",
        "                    else:\n",
        "                        text2img_pipes[seamless].disable_attention_slicing()\n",
        "                    # NSFWの制限を外す\n",
        "                    text2img_pipes[seamless].safety_checker = lambda images, **kwargs: (images, False)\n",
        "                    image = text2img_pipes[seamless](\n",
        "                        prompt,\n",
        "                        width=w,\n",
        "                        height=h,\n",
        "                        num_inference_steps=n_steps,\n",
        "                        guidance_scale=g_scale,\n",
        "                        generator=generator,\n",
        "                    )[\"sample\"][0]\n",
        "            except ValueError as e:\n",
        "                print(\"■■■■■■■■■■■■■■■\")\n",
        "                print(\"■　** ERROR **\")\n",
        "                print(\"■　与えられたパラメーターが正しくなくて、画像生成に失敗しました。\")\n",
        "                print(f\"■　エラーメッセージ：{e}\")\n",
        "                print(\"■　---------------\")\n",
        "                print(\"■　　メッセージ別対応方法\")\n",
        "                print(\n",
        "                    \"■　　　ValueError: `height` and `width` have to be divisible by 8 ... 画像サイズを任意入力した場合、サイズの縦横両方ともが 64 で割り切れる整数にしてください\"\n",
        "                )\n",
        "                print(\"■■■■■■■■■■■■■■■\")\n",
        "                raise IpyExit\n",
        "            except RuntimeError as e:\n",
        "                print(e)\n",
        "                print(traceback.format_exc())\n",
        "                print(\"■■■■■■■■■■■■■■■\")\n",
        "                print(\"■　** ERROR **\")\n",
        "                print(\"■　GPU上での処理時に、画像生成に失敗しました。\")\n",
        "                print(\"■　---------------\")\n",
        "                print(\"■　　メッセージ別対応方法\")\n",
        "                print(\n",
        "                    \"■　　　RuntimeError: CUDA out of memory. ... メモリ不足です。実行キャパシティの各値を小さくしてください。\"\n",
        "                )\n",
        "                print(\n",
        "                    \"■　　　Sizes of tensors must match except in dimension 1.　... アスペクト比に基づいた画像サイズで推定ができませんでした。\"\n",
        "                )\n",
        "                print(\n",
        "                    \"■　　　RuntimeError: Given groups=1, weight of size　... RGBA画像で画像が指定されています。アルファチャンネルを削除してください。\"\n",
        "                )\n",
        "                print(\"■■■■■■■■■■■■■■■\")\n",
        "                raise IpyExit\n",
        "            except Exception as e:\n",
        "                print(e)\n",
        "                print(traceback.format_exc())\n",
        "                print(\"■■■■■■■■■■■■■■■\")\n",
        "                print(\"■　** ERROR **\")\n",
        "                print(\"■　予期せぬ例外により画像生成に失敗しました。\")\n",
        "                print(\"■■■■■■■■■■■■■■■\")\n",
        "                raise IpyExit\n",
        "\n",
        "            file_path = (\n",
        "                f\"{output_dir}/StableDiffusionGenerator_{now_str}_{(n + 1):05d}.png\"\n",
        "            )\n",
        "            # 保存\n",
        "            image.save(file_path)\n",
        "            # パス表示\n",
        "            print(\"-------------------------------------\")\n",
        "            print(f\"■ {file_path}\")\n",
        "            file_paths.append(file_path)\n",
        "            # 画像表示\n",
        "            display(image)\n",
        "\n",
        "    # セル終了\n",
        "    finish_cell(ckey)\n",
        "\n",
        "    return file_paths\n",
        "\n",
        "# ------------------------\n",
        "jp_prompts = []\n",
        "other_promts = []\n",
        "seed = p_manual_seed = 0\n",
        "saving = p_saving = False\n",
        "os.makedirs(\"/content/outputs\", exist_ok=True)\n",
        "\n",
        "# セル終了\n",
        "finish_cell(ckey)"
      ],
      "metadata": {
        "id": "trQPKRCm4_rn",
        "cellView": "form"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "# ③　Stable Diffusion 実行（txt2img）"
      ],
      "metadata": {
        "id": "bLehFB0O2OZI"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "## ③-A　画像の生成"
      ],
      "metadata": {
        "id": "4E47I8pXPUAv"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "#@markdown ■■■■■■■■■■■■■■■■■■\n",
        "\n",
        "#@markdown このセルを実行すると、`image_nums` に指定された枚数の画像を生成します。\n",
        "\n",
        "#@markdown 呪文（油彩やテキスタイル等、生成したい画像イメージ）を指定するとそのイメージに合った画像を作成します。\n",
        "\n",
        "#@markdown タッチなどのプルダウンは右端の小さな▼で選択肢が選べます。（任意入力OKの項目は自由に日本語指定可能です）\n",
        "\n",
        "#@markdown 画像生成枚数などのスライダーは右端に小さく現在の数値が出ています。\n",
        "\n",
        "#@markdown ■■■■■■■■■■■■■■■■■■\n",
        "\n",
        "#@markdown ### 呪文\n",
        "\n",
        "#@markdown 日本語での呪文（文章・単語・フレーズ）など、自由に入力してください。（詳細は次のセルに記載）\n",
        "p_japanese_prompt = \"\\u6B63\\u9762\\u3092\\u5411\\u3044\\u3066\\u5FAE\\u7B11\\u3080\\u7F8E\\u3057\\u3044\\u5C11\\u5973\\u3000\\u91D1\\u9AEA\\u306E\\u30ED\\u30F3\\u30B0\\u30D8\\u30A2\\u3000\\u7DD1\\u8272\\u306E\\u77B3\\u3000\\u7070\\u8272\\u306E\\u30EF\\u30F3\\u30D4\\u30FC\\u30B9\\u3000\\u53E4\\u3044\\u30AB\\u30D5\\u30A7\\u306E\\u7A93\\u8FBA\\u306E\\u5E2D\\u3000\\u5DEE\\u3057\\u8FBC\\u3080\\u65E5\\u5DEE\\u3057\\u3000\\u53E4\\u3044\\u672C\\u304C\\u4E26\\u3079\\u3089\\u308C\\u305F\\u672C\\u68DA\" #@param {type:\"string\"}\n",
        "\n",
        "#@markdown 世界観（選択・任意入力OK）\n",
        "p_world = \"\" #@param [\"\", \"ファンタジー\", \"サイバーパンク\", \"中世風\", \"近未来\", \"スチームパンク\", \"アーバンファンタジー\", \"ジュブナイル\", \"メルヘン\", \"ダークメルヘン\", \"和風\", \"和風ファンタジー\", \"中華風\", \"中華風ファンタジー\", \"大正浪漫\", \"SF\", \"ディストピア\", \"ポストアポカリプス\", \"ダークファンタジー\"] {allow-input: true}\n",
        "\n",
        "#@markdown 種類（選択・任意入力OK）\n",
        "p_image_type = \"\\u30DD\\u30B9\\u30BF\\u30FC\" #@param [\"\", \"写真\", \"ポスター\", \"テクスチャ\", \"カートゥーン\", \"アメリカンコミック\", \"少女コミック\", \"少年コミック\", \"アニメ\", \"古い映画\", \"シルエット\", \"ポートレート\", \"カタログ写真\", \"風景画\", \"静物画\", \"肖像画\", \"博物画\", \"美人画\", \"花鳥画\"] {allow-input: true}\n",
        "\n",
        "#@markdown 技法（選択・任意入力OK）\n",
        "p_technique = \"\" #@param [\"\", \"日本画\", \"洋画\", \"油彩\", \"水彩画\", \"水墨画\", \"版画\", \"ステンドグラス\", \"モザイクタイル\", \"コラージュ\", \"セルルック\", \"切り絵\", \"ちぎり絵\", \"砂絵\", \"シンメトリー\"] {allow-input: true}\n",
        "\n",
        "#@markdown 画材（選択・任意入力OK）\n",
        "p_supply = \"\" #@param [\"\", \"鉛筆\", \"絵の具\", \"木炭\", \"パステル\", \"クレヨン\", \"ペン\", \"チョーク\", \"エアブラシ\", \"アクリルガッシュ\", \"マットな塗り\", \"厚塗り\"] {allow-input: true}\n",
        "\n",
        "#@markdown 画風（選択・任意入力OK）\n",
        "p_school = \"\" #@param [\"\", \"印象派\", \"アール・ヌーヴォー\", \"ポップアート\", \"オプアート\", \"ミニマルアート\", \"コンセプチュアルアート\", \"ボタニカルアート\", \"大和絵\", \"浮世絵\", \"源氏絵\", \"アンリアル・エンジン\"] {allow-input: true}\n",
        "\n",
        "#@markdown 写真系加工（選択・任意入力OK）\n",
        "p_photo = \"\" #@param [\"\", \"ソフトフォーカス\", \"シズル感\", \"ノイズ\", \"手ぶれ\", \"モノクロ\", \"セピア\", \"レトロ\", \"リムライト\", \"リアリスティック\"] {allow-input: true}\n",
        "\n",
        "#@markdown 視点（選択・任意入力OK）\n",
        "p_perspective = \"\" #@param [\"\", \"煽り\", \"俯瞰\", \"バストアップ\", \"顔写真\", \"全身\", \"横顔\", \"正面\", \"後ろ姿\"] {allow-input: true}\n",
        "\n",
        "#@markdown カメラ（選択・任意入力OK）\n",
        "p_camera = \"\" #@param [\"\", \"Canon EOS 5D\", \"Nikon D750\", \"Sony α7\"] {allow-input: true}\n",
        "\n",
        "#@markdown レンズの焦点距離（選択・任意入力OK）\n",
        "p_length = \"\" #@param [\"\", \"50mm: かつての標準距離\", \"35mm: iPhoneのカメラ距離\", \"80mm: ボケを深くして背景をグッと圧縮したいときの距離\"] {allow-input: true}\n",
        "\n",
        "#@markdown ボケ具合（選択・任意入力OK）\n",
        "p_blur = \"\" #@param [\"\", \"f1.8: ボケ味深め\", \"f4: ズームレンズ\", \"f8: クッキリ見せたい\", \"f22: 風景など、光が多い場所で全体にピンを合わせたい\"] {allow-input: true}\n",
        "\n",
        "#@markdown 色調（選択・任意入力OK）\n",
        "p_tone = \"\" #@param [\"\", \"ビビッドトーン: 鮮やかで強い色\", \"ブライトトーン: 明るいにぎやかな色\", \"ストロングトーン: 強烈な色\", \"ディープトーン: 濃い深みのある色\", \"ライトトーン: 明るく軽い色\", \"ソフトトーン: 柔らかい色\", \"ダルトーン: さえない穏やかな色\", \"ダークトーン: 重厚で暗い色\", \"ペールトーン: 淡いクリアな色\", \"ライトグレイッシュトーン: 穏やかな色\", \"グレイッシュトーン: 地味目で渋い色\", \"ダークグレイッシュトーン: 濃く重い色\", \"ベリーペールトーン: 淡く薄い色\", \"ペールグレイッシュトーン: 淡く鈍い色\", \"ミドルグレイッシュトーン: やや濁った色\", \"ベリーグレイッシュトーン: とても渋い色\", \"ベリーダークトーン: 濃く深い色\"] {allow-input: true}\n",
        "\n",
        "#@markdown 色相（選択・任意入力OK）\n",
        "p_hue = \"\" #@param [\"\", \"赤\", \"青\", \"緑\", \"黄\", \"紫\", \"橙\", \"黒\", \"灰\", \"白\"] {allow-input: true}\n",
        "\n",
        "#@markdown 特定作家・ブランド風（選択・任意入力OK）\n",
        "p_author = \"\" #@param [\"\", \"クロード・モネ\", \"アルフォンス・ミュシャ\", \"レンブラント・ファン・レイン\", \"ヨハネス・フェルメール\", \"レオナルド・ダ・ヴィンチ\", \"ピエール＝オーギュスト・ルノワール\" , \"フィンセント・ファン・ゴッホ\" , \"グスタフ・クリムト\" , \"パブロ・ピカソ\" , \"アンディ・ウォーホル\", \"葛飾北斎\", \"新海誠\", \"宮崎駿\", \"桂由美\", \"イームズ\", \"SONY\"] {allow-input: true}\n",
        "\n",
        "#@markdown 翻訳なしの呪文（英語のニュアンスを意識して指定したい場合に使用してください）\n",
        "p_english_prompt = \"\" #@param {type:\"string\"}\n",
        "\n",
        "#@markdown ### 画像の特徴\n",
        "\n",
        "#@markdown シームレス（継ぎ目なく敷き詰められる）画像であるか否か\n",
        "p_seamless = False #@param {type:\"boolean\"}\n",
        "\n",
        "#@markdown 細かく書き込みされた画像であるか否か\n",
        "p_detail = False #@param {type:\"boolean\"}\n",
        "\n",
        "#@markdown 人物の顔パーツ（目・耳など）を指定するか\n",
        "p_face = True #@param {type:\"boolean\"}\n",
        "\n",
        "#@markdown 人物の上半身パーツ（胸・腕など）を指定するか\n",
        "p_upper = True #@param {type:\"boolean\"}\n",
        "\n",
        "#@markdown 人物の下半身パーツ（腰など）を指定するか\n",
        "p_lower = False #@param {type:\"boolean\"}\n",
        "\n",
        "#@markdown 人物の足パーツ（踵など）を指定するか\n",
        "p_leg = False #@param {type:\"boolean\"}\n",
        "\n",
        "#@markdown イラスト風人物（デフォルトのStable Diffusion モデルを使用している場合に有効）\n",
        "p_illust = False #@param {type:\"boolean\"}\n",
        "\n",
        "#@markdown ### 画像サイズ\n",
        "\n",
        "#@markdown アスペクト比（横:縦）\n",
        "p_aspect = \"1:1\"  #@param ['1:1', '2:1', '3:2', '4:3', '16:9', '1:2', '2:3', '3:4', '9:16']\n",
        "\n",
        "#@markdown 画像横幅（縦幅はアスペクト比を加味して算出します。任意入力OKですが、縦横いずれも64の倍数になるようにしてください。）\n",
        "p_width = \"512\" #@param [\"320\", \"512\", \"768\", \"1280\", \"1920\", \"2560\", \"3840\"] {allow-input: true}\n",
        "\n",
        "#@markdown ### 画像生成枚数\n",
        "\n",
        "#@markdown （値が大きいほど沢山の画像が生成されます）\n",
        "p_image_nums = 5 #@param {type:\"slider\", min:1, max:20}\n",
        "\n",
        "#@markdown ### 推定パラメーター\n",
        "\n",
        "#@markdown 試行回数\n",
        "p_num_inference_steps = 50 #@param {type:\"slider\", min:1, max:200}\n",
        "\n",
        "#@markdown 試行精度\n",
        "p_guidance_scale = 7.6 #@param {type:\"slider\", min:0.1, max:30, step: 0.1}\n",
        "\n",
        "#@markdown 省エネモード（チェックをONにすると、少ないリソースで実行します。Colab上だと試行枚数が多くできます。ただし少し遅くなります。）\n",
        "p_saving = False #@param {type:\"boolean\"}\n",
        "\n",
        "#@markdown シード値（0の場合はランダムシードを生成します）\n",
        "p_manual_seed = 0 #@param {type:\"slider\", min:0, max:1024, step: 1}\n",
        "\n",
        "# セル実行可否\n",
        "ckey = \"③-A\"\n",
        "check_exec_dict(ckey)\n",
        "\n",
        "init_text2img_pipe()\n",
        "\n",
        "# --------------------------\n",
        "# prompt生成\n",
        "prompt, seed, jp_prompts, other_promts, w, h = create_prompt(japanese_prompt=p_japanese_prompt, world=p_world, image_type=p_image_type, technique=p_technique, supply=p_supply, school=p_school, photo=p_photo, perspective=p_perspective, camera=p_camera, length=p_length, blur=p_blur, tone=p_tone, hue=p_hue, author=p_author, english_prompt=p_english_prompt, seamless=p_seamless, detail=p_detail, face=p_face, upper=p_upper, lower=p_lower, leg=p_leg, illust=p_illust, aspect=p_aspect, width=p_width, image_nums=p_image_nums, num_inference_steps=p_num_inference_steps, guidance_scale=p_guidance_scale, manual_seed=p_manual_seed)\n",
        "\n",
        "# AI実行\n",
        "file_paths = execute(prompt, ', '.join(jp_prompts), p_seamless, seed, p_aspect, w, h, p_image_nums, p_num_inference_steps, p_guidance_scale, p_saving)\n",
        "\n",
        "# チューニングスライダー追加\n",
        "tuning_widgets = []\n",
        "tuning_slider = []\n",
        "for p in jp_prompts:\n",
        "  tuning_widgets.append(Label(f'{p} が占める割合:'))\n",
        "  s = FloatSlider(value=1.0, min=0, max=1, step=0.1, readout_format='.1f')\n",
        "  tuning_widgets.append(s)\n",
        "  tuning_slider.append(s)\n",
        "\n",
        "display(GridBox(tuning_widgets, layout=widgets.Layout(grid_template_columns=\"repeat(2, 30%)\")))\n",
        "\n",
        "\n",
        "print(\"■■■■■■■■■■■■■■■\")\n",
        "print(\"■　一度画像を生成した後、呪文の各要素強度を指定してチューニングできます。\")\n",
        "print(\"■　スライダーを動かして強度を指定した後、【③-B】のセルを実行してください。\")\n",
        "print(\"■■■■■■■■■■■■■■■\")\n"
      ],
      "metadata": {
        "id": "oQNa5qXL89Ow",
        "cellView": "form"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## ③-B　画像のチューニング"
      ],
      "metadata": {
        "id": "wAVi9CuOPixl"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "#@markdown ■■■■■■■■■■■■■■■■■■\n",
        "\n",
        "#@markdown 【③-A】で生成した画像の呪文の各要素強度を指定してチューニングできます。\n",
        "\n",
        "#@markdown ■■■■■■■■■■■■■■■■■■\n",
        "\n",
        "# セル実行可否\n",
        "ckey = \"③-B\"\n",
        "check_exec_dict(ckey)\n",
        "\n",
        "# 翻訳したのをリストアップ\n",
        "prompts = []\n",
        "prompts.append(\", \".join([f\"( {translate_jp2en(p)} ) * {w.value}\" for (p, w) in zip(jp_prompts, tuning_slider)]))\n",
        "prompts.append(\", \".join(other_promts))\n",
        "\n",
        "prompt = \", \".join(prompts)\n",
        "\n",
        "execute(\n",
        "    prompt,\n",
        "    \", \".join(jp_prompts),\n",
        "    p_seamless,\n",
        "    seed,\n",
        "    p_aspect,\n",
        "    w,\n",
        "    h,\n",
        "    p_image_nums,\n",
        "    p_num_inference_steps,\n",
        "    p_guidance_scale,\n",
        "    p_saving,\n",
        ")\n"
      ],
      "metadata": {
        "cellView": "form",
        "id": "emZYgNNl3WRj"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## イメージ指示（Prompt）の出し方\n",
        "\n",
        " 1. 日本語文章でイメージが指定できます\n",
        "     - `金髪の美少女がレトロなカフェでお茶を飲んでいる`\n",
        "     - `僕はもうあの蠍のように本当にみんなの幸のためならば僕の身体なんか百篇灼いてもかまわない。`　（「銀河鉄道の夜」宮沢賢治）\n",
        "     - `田子の浦にうちいでて見れば白たへの富士の高嶺に雪は降りつつ`　（山部赤人）\n",
        "\n",
        " 1. 文章がうまく翻訳できない場合、空白で区切って短いフレーズで出してください\n",
        "    - `金髪の美少女　レトロなカフェ　ティータイム`\n",
        "    - `ロングスカートのメイドの少女　黒髪　ロングヘア　静かに微笑む`\n",
        "    - `Ａライン ミニマリスト フォーマルイブニング ドレス ホルター ノースリーブ フロア丈 シフォン クリスタル装飾`\n",
        "        - 衣装や椅子などのアイテムはショッピングサイトの検索ワード列挙文が参考になりそうです\n",
        "\n",
        "    - 全角空白で区切った文章は別の意味（別のカッコ）で括ります\n",
        "    - `!` をフレーズに追加すると、そのフレーズが強調されます\n",
        "\n",
        " 1. 言葉の意味の足し引きができます。\n",
        "      - `(金髪の美女 - 少女)　(レトロ + カフェ)　ティータイム`\n",
        "      - 演算子は半角で入力して、前後に空白を入れる場合も半角にしてください\n"
      ],
      "metadata": {
        "id": "9xOw_gCakgVw"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "## その他Tips\n",
        " \n",
        " 1. 実際に Stable Diffusion に指示したPromptは　実行画面先頭の `■ PROMPT` に記載しています\n",
        "   - シード値も　`■ SEED` で出しているので、出力結果と似たような結果が欲しい場合には、そのシード値を `manual_seed` に設定すると、シード値を固定する事ができます。\n",
        " 1. 左側にコードが出てしまった場合、右側の白い部分をダブルクリックすると戻ります\n",
        " 1. 左上のフォルダアイコン ＞ outputs から生成したPromptと生成画像が確認できます。\n",
        "   - ファイルをダブルクリックすると、画面右側に表示されます。"
      ],
      "metadata": {
        "id": "HxTK12kYHQwm"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "# ④　Stable Diffusion 実行（img2img）"
      ],
      "metadata": {
        "id": "C3beHD9HVSh9"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "## ④-A　img2img用UIの表示"
      ],
      "metadata": {
        "id": "MvSoUI3AP_2k"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "#@markdown ■■■■■■■■■■■■■■■■■■\n",
        "\n",
        "#@markdown このセルを実行すると起点画像を選ぶためのUIが表示されます。\n",
        "\n",
        "#@markdown ③で出力した画像を想定していますが、 `outputs` の中にアップロードされた png画像 であれば生成対象とすることができます。\n",
        "\n",
        "#@markdown ■■■■■■■■■■■■■■■■■■\n",
        "\n",
        "\n",
        "from ipywidgets import Label, Layout, HBox, GridBox, interact, BoundedIntText\n",
        "from IPython.display import display\n",
        "from glob import glob\n",
        "from PIL import Image\n",
        "\n",
        "# セル実行可否\n",
        "ckey = \"④-A\"\n",
        "check_exec_dict(ckey)\n",
        "\n",
        "init_img2img_pipe()\n",
        "\n",
        "animation_widgets = []\n",
        "\n",
        "images = list(sorted([n for n in glob(\"/content/outputs/**/*.png\", recursive=True)]))\n",
        "if not images:\n",
        "    print(\"■■■■■■■■■■■■■■■\")\n",
        "    print(\"■　** WARNING **\")\n",
        "    print(\"■　outputs フォルダの中に png 画像が見つかりませんでした。\")\n",
        "    print(\"■　png 画像をアップロードした後、再度このセルを実行するとプルダウンの中に選択肢として画像ファイル名が読み込まれます。\")\n",
        "    print(\"■■■■■■■■■■■■■■■\")\n",
        "    raise IpyExit\n",
        "\n",
        "%cd /content/\n",
        "\n",
        "print(\"-------------------------------------\")\n",
        "print(f\"■ 起点画像\")\n",
        "\n",
        "def load_init_img(img_path: str):\n",
        "    if not img_path:\n",
        "        return widgets.Image()\n",
        "\n",
        "    img_bytes = None\n",
        "    with open(img_path, \"rb\") as f:\n",
        "        img_bytes = f.read() \n",
        "    pil_img = Image.open(img_path)\n",
        "\n",
        "    img = widgets.Image(\n",
        "        value=img_bytes,\n",
        "        format='png',\n",
        "        width=int(pil_img.size[0] / 3),\n",
        "        height=int(pil_img.size[1] / 3),\n",
        "    )\n",
        "\n",
        "    return img\n",
        "\n",
        "init_image_dd = widgets.Dropdown(\n",
        "    options=images,\n",
        "    value=images[0],\n",
        "    disabled=False,\n",
        "    layout=widgets.Layout(width='600px', height='auto')\n",
        ")\n",
        "interact(load_init_img, img_path=init_image_dd)\n",
        "\n",
        "print(\"■■■■■■■■■■■■■■■\")\n",
        "print(\"■　起点画像を選んだ後、次の【④-B】のセルを実行してください。\")\n",
        "print(\"■■■■■■■■■■■■■■■\")\n",
        "\n",
        "# セル終了\n",
        "finish_cell(ckey)\n"
      ],
      "metadata": {
        "id": "QIZQQnpAgf1j",
        "cellView": "form"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## ④-B　img2imgの実行"
      ],
      "metadata": {
        "id": "p423gr5jQa0H"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "#@markdown ■■■■■■■■■■■■■■■■■■\n",
        "\n",
        "#@markdown 呪文や変化度などを入力してから、セルを実行してください\n",
        "\n",
        "#@markdown ■■■■■■■■■■■■■■■■■■\n",
        "\n",
        "#@markdown ### 呪文\n",
        "\n",
        "#@markdown 変化後の画像イメージを日本語もしくは英語の呪文（文章・単語・フレーズ）で入力してください\n",
        "p_prompt = \"\\u8594\\u8587\\u306E\\u82B1\\u675F\\u3092\\u6301\\u3063\\u305F\\u5C11\\u5973\" #@param {type:\"string\"}\n",
        "\n",
        "#@markdown ### 変化度\n",
        "\n",
        "#@markdown 元画像からの変化度を指定してください。変化度=0の場合、変化度を徐々に大きくさせて生成します。\n",
        "\n",
        "#@markdown ～0.2: ほとんど変化しない, ～0.3: パーツが若干変化する, ～0.6: 元絵の雰囲気を残しつつ絵柄が変わる, ～0.8: 元絵の雰囲気があまり残らなくなる, 0.9: 元絵をほぼ無視する\n",
        "\n",
        "p_strength = 0.5 #@param {type:\"slider\", min:0, max:1, step: 0.01}\n",
        "\n",
        "#@markdown ### 画像生成枚数\n",
        "\n",
        "#@markdown （値が大きいほど沢山の画像が生成されます）\n",
        "p_image_nums = 3 #@param {type:\"slider\", min:1, max:20}\n",
        "\n",
        "#@markdown ### 推定パラメーター\n",
        "\n",
        "#@markdown 試行回数\n",
        "p_num_inference_steps = 100 #@param {type:\"slider\", min:1, max:200}\n",
        "\n",
        "#@markdown 試行精度\n",
        "p_guidance_scale = 15.0 #@param {type:\"slider\", min:0.1, max:30, step: 0.1}\n",
        "\n",
        "#@markdown シード値（0の場合はランダムシードを生成します）\n",
        "p_manual_seed = 0 #@param {type:\"slider\", min:0, max:1024, step: 1}\n",
        "\n",
        "#@markdown 生成画像を連結させた動画のfps（少ないほどゆっくり切り替わります）\n",
        "p_fps = 2 #@param {type:\"slider\", min:1, max:30}\n",
        "\n",
        "\n",
        "# セル実行可否\n",
        "ckey = \"④-B\"\n",
        "check_exec_dict(ckey)\n",
        "\n",
        "# ---------\n",
        "from IPython.display import HTML\n",
        "from base64 import b64encode\n",
        "from PIL import Image\n",
        "\n",
        "pil_img = Image.open(init_image_dd.value)\n",
        "\n",
        "w = pil_img.size[0]\n",
        "h = pil_img.size[1]\n",
        "if w % 64 or h % 64:\n",
        "    print(\"■■■■■■■■■■■■■■■\")\n",
        "    print(\"■　** ERROR **\")\n",
        "    print(\"■　画像サイズの縦横が64で割り切れない値になっています。\")\n",
        "    print(\"■　画像サイズを見直して下さい。\")\n",
        "    print(f\"■　画像横幅: {w}({w / 64:.3f}), 画像縦幅: {h}({h / 64:.3f})\")\n",
        "    print(\"■■■■■■■■■■■■■■■\")\n",
        "    raise IpyExit\n",
        "\n",
        "seed = p_manual_seed if p_manual_seed else random.randint(1, 1024)\n",
        "saving = p_saving if p_saving else False\n",
        "\n",
        "file_paths = execute(\n",
        "    translate_jp2en(p_prompt),\n",
        "    p_prompt,\n",
        "    False,\n",
        "    seed,\n",
        "    \"\",\n",
        "    w,\n",
        "    h,\n",
        "    p_image_nums,\n",
        "    p_num_inference_steps,\n",
        "    p_guidance_scale,\n",
        "    img2img=True,\n",
        "    init_image_path=init_image_dd.value,\n",
        "    strength=p_strength,\n",
        "    saving=saving,\n",
        ")\n",
        "\n",
        "# 出力画像を結合\n",
        "file_prefix = file_paths[0][:-9]\n",
        "output_mp4 = os.path.join(os.path.dirname(file_paths[0]), \"img2img.mp4\")\n",
        "! ffmpeg -r $p_fps -i $file_prefix%05d.png -vcodec libx264 -pix_fmt yuv420p -r 30 $output_mp4\n",
        "\n",
        "mp4 = open(output_mp4, 'rb').read()\n",
        "data_url = 'data:video/mp4;base64,' + b64encode(mp4).decode()\n",
        "display(HTML(f\"\"\"\n",
        "<video controls>\n",
        "        <source src=\"{data_url}\" type=\"video/mp4\">\n",
        "</video>\"\"\"))\n"
      ],
      "metadata": {
        "cellView": "form",
        "id": "Cuthxgu72WRr"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## outputs へのアップロード方法\n",
        "\n",
        "左上のフォルダアイコン＞outputs＞アップロード　で任意の画像をアップロードすることができます。\n",
        "\n",
        "![フォルダ](https://drive.google.com/uc?export=view&id=1lF_o7e3IzI0lq_yyJf_ac0qRQA2bsFIa)\n",
        "\n",
        "![outputs](https://drive.google.com/uc?export=view&id=1eI6VAFn9RBj6RJ2L53hXUVVQeMoqPLCk)\n",
        "\n",
        "![outputs](https://drive.google.com/uc?export=view&id=1f1ViID6oEV-EW3qcAuCGzHOmZkkgwu7V)\n",
        "\n",
        "### 読み込める画像の種類\n",
        "\n",
        " - 画像の大きさが小さいこと（大きいとメモリオーバーでクラッシュしやすいです）\n",
        " - 画像サイズの縦横が64で割り切れる事"
      ],
      "metadata": {
        "id": "zYDlS-9FT4Jp"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "# ⑤　Stable Diffusion 実行（inpainting）"
      ],
      "metadata": {
        "id": "W8dLI-rG1Kyz"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "下の【⑤】を実行すると、inpainting用UIが出力されます。"
      ],
      "metadata": {
        "id": "dQdlkcMS1QQU"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "## ⑤-A　inpainting用UIの表示"
      ],
      "metadata": {
        "id": "y27FbSkOSL74"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "#@markdown ■■■■■■■■■■■■■■■■■■\n",
        "\n",
        "#@markdown このセルを実行すると起点となる画像・どの部分だけ修正するか（マスク画像）のUIが表示されます。\n",
        "\n",
        "#@markdown ③で出力した画像を想定していますが、 `outputs` の中にアップロードされた png画像 であれば生成対象とすることができます。\n",
        "\n",
        "#@markdown ■■■■■■■■■■■■■■■■■■\n",
        "\n",
        "\n",
        "from ipywidgets import Label, Layout, HBox, GridBox, interact, BoundedIntText\n",
        "from google.colab.output import eval_js\n",
        "import IPython\n",
        "from IPython.display import display, HTML, Image, Javascript\n",
        "from glob import glob\n",
        "from PIL import Image\n",
        "from io import BytesIO\n",
        "import base64\n",
        "from base64 import b64decode\n",
        "\n",
        "# セル実行可否\n",
        "ckey = \"⑤-A\"\n",
        "check_exec_dict(ckey)\n",
        "\n",
        "init_inpainting_pipe()\n",
        "\n",
        "def image_file_to_base64(file_path):\n",
        "    with open(file_path, \"rb\") as image_file:\n",
        "        data = base64.b64encode(image_file.read())\n",
        "    return \"data:image/png;base64,\" + data.decode('utf-8')\n",
        "\n",
        "def base64_to_pil(img_str):\n",
        "    if \"base64,\" in img_str:\n",
        "        # DARA URI の場合、data:[<mediatype>][;base64], を除く\n",
        "        img_str = img_str.split(\",\")[1]\n",
        "    img_raw = base64.b64decode(img_str)\n",
        "    img = Image.open(BytesIO(img_raw))\n",
        "\n",
        "    return img\n",
        "\n",
        "def save_mask_image(base64str: str):\n",
        "    im = base64_to_pil(base64str)\n",
        "    im.save(\"/content/mask.jpg\", 'jpeg')\n",
        "\n",
        "    with open('/content/mask.jpg', 'rb') as f:\n",
        "        pil_img = Image.open(f, 'r')\n",
        "        display(pil_img)\n",
        "\n",
        "    print(\"■■■■■■■■■■■■■■■\")\n",
        "    print(\"■　マスク画像を保存しました。\")\n",
        "    print(\"■■■■■■■■■■■■■■■\")\n",
        "\n",
        "    # セル終了\n",
        "    finish_cell(ckey)\n",
        "\n",
        "def save_init_image(base64str: str):\n",
        "    im = base64_to_pil(base64str)\n",
        "    im.save(\"/content/init.png\", 'PNG')\n",
        "\n",
        "from google.colab import output\n",
        "output.register_callback('notebook.mask_image', save_mask_image)\n",
        "output.register_callback('notebook.init_image', save_init_image)\n",
        "\n",
        "images = list(sorted([n for n in glob(\"/content/outputs/**/*.png\", recursive=True)]))\n",
        "if not images:\n",
        "    print(\"■■■■■■■■■■■■■■■\")\n",
        "    print(\"■　** WARNING **\")\n",
        "    print(\"■　outputs フォルダの中に png 画像が見つかりませんでした。\")\n",
        "    print(\"■　png 画像をアップロードした後、再度このセルを実行するとプルダウンの中に選択肢として画像ファイル名が読み込まれます。\")\n",
        "    print(\"■■■■■■■■■■■■■■■\")\n",
        "    raise IpyExit\n",
        "\n",
        "options_html = \"\".join([f\"<option value=\\\"{image_file_to_base64(path)}\\\">{path}</option>\" for path in images])\n",
        "\n",
        "!cd /content/\n",
        "\n",
        "if not os.path.exists(\"sketchable.full.min.js\"):\n",
        "    # sketchableを取得\n",
        "    ! wget --no-check-certificate -c \"https://raw.githubusercontent.com/luileito/jsketch/master/dist/sketchable.full.min.js\"\n",
        "\n",
        "sketchable_script = \"\"\n",
        "with open(\"sketchable.full.min.js\", \"r\") as f:\n",
        "    sketchable_script = f.read()\n",
        "\n",
        "canvas_html = '''\n",
        "<script type=\"text/javascript\">%s</script>\n",
        "<div id=\"container\" style=\"overflow: visible; resize: both;\">\n",
        "    <div class=\"center\" id=\"brushes\">\n",
        "        <strong>Brush type:</strong>\n",
        "        <label>\n",
        "            <input type=\"radio\" name=\"brush\" id=\"brush_pencil\" value=\"Pencil\" checked=\"checked\" />\n",
        "            ペン\n",
        "        </label>\n",
        "        <label>\n",
        "            <input type=\"radio\" name=\"brush\" id=\"brush_eraser\" value=\"Eraser\" />\n",
        "            消しゴム\n",
        "        </label>\n",
        "        <label>\n",
        "            <input type=\"radio\" name=\"brush\" id=\"brush_fill\" value=\"Fill\" />\n",
        "            四角形\n",
        "        </label>\n",
        "    </div>\n",
        "    <div class=\"controls\">\n",
        "        <input type=\"button\" id=\"clear\" value=\"初期化\">\n",
        "        <input type=\"button\" id=\"undo\" value=\"元に戻す\">\n",
        "        <input type=\"button\" id=\"redo\" value=\"やり直す\">\n",
        "        <input type=\"button\" id=\"save\" value=\"保存\">\n",
        "    </div>\n",
        "    <input type=\"hidden\" name=\"start_x\" />\n",
        "    <input type=\"hidden\" name=\"start_y\" />\n",
        "    <input type=\"hidden\" name=\"mask_image\" />\n",
        "    <select id=\"file_path_selects\">\n",
        "        %s\n",
        "    </select>\n",
        "    <div id=\"canvas-wrap\" width=\"320px\" height=\"320px\" style=\"position: relative; overflow: visible; resize: both;\">\n",
        "        <!-- 描画Canvas -->\n",
        "        <canvas id=\"picture_canvas\" width=\"320px\" height=\"320px\" style=\"overflow: visible; resize: both;\"></canvas>\n",
        "        <!-- 一時的描画Canvas -->\n",
        "        <canvas id=\"drawing_canvas\" width=\"1024px\" height=\"1024px\" style=\"position: absolute; top: 0px; left: 0px; overflow: visible; resize: both;\"></canvas>\n",
        "    </div>\n",
        "</div>\n",
        "\n",
        "<script type=\"text/javascript\"> \n",
        "    const imageCanvas = document.getElementById('picture_canvas');\n",
        "    const imageCtx = imageCanvas.getContext('2d');\n",
        "    const drawCanvas = document.getElementById('drawing_canvas');\n",
        "    const drawCtx = drawCanvas.getContext('2d');\n",
        "\n",
        "    var sketcher = new Sketchable('#drawing_canvas', {\n",
        "        graphics: {\n",
        "            firstPointSize: 1,\n",
        "            lineWidth: 20,\n",
        "            strokeStyle: 'white',\n",
        "            fillStyle: 'white',\n",
        "            lineCap: 'round',\n",
        "            lineJoin: 'round',\n",
        "            miterLimit: 10\n",
        "        },\n",
        "        events: {\n",
        "            // We use the \"before\" event hook to update brush type right before drawing starts.\n",
        "            mousedownBefore: function (elem, data, evt) {\n",
        "                var brushType = getBrushType();\n",
        "                if (brushType == 'eraser') {\n",
        "                    // There is a method to set the brush in eraser mode.\n",
        "                    data.options.graphics.lineWidth = 20;\n",
        "                    data.sketch.eraser();\n",
        "                } else if (brushType == 'pencil') {\n",
        "                    // inputRecorder.resume();\n",
        "                    // There is a method to get the default mode (pencil) back.\n",
        "                    data.options.graphics.lineWidth = 20;\n",
        "                    data.sketch.pencil();\n",
        "                } else if (brushType == 'fill') {\n",
        "                    // inputRecorder.resume();\n",
        "                    // There is a method to get the default mode (pencil) back.\n",
        "                    data.options.graphics.lineWidth = 1;\n",
        "                    data.sketch.pencil();\n",
        "                    document.querySelector('input[name=\"start_x\"]').value = evt.offsetX;\n",
        "                    document.querySelector('input[name=\"start_y\"]').value = evt.offsetY;\n",
        "                }\n",
        "            },\n",
        "            mouseup: function (elem, data, evt) {\n",
        "                evt.preventDefault();\n",
        "                var brushType = getBrushType();\n",
        "                if (brushType == 'fill') {\n",
        "                    var start_x = document.querySelector('input[name=\"start_x\"]').value;\n",
        "                    var start_y = document.querySelector('input[name=\"start_y\"]').value;\n",
        "                    var endX = evt.offsetX;\n",
        "                    var endY = evt.offsetY;\n",
        "\n",
        "                    var x = start_x > endX ? start_x : endX;\n",
        "                    var width = start_x > endX ? endX - start_x : start_x - endX;\n",
        "                    var y = start_y > endY ? start_y : endY;\n",
        "                    var height = start_y > endY ? endY - start_y : start_y - endY;\n",
        "\n",
        "                    data.sketch\n",
        "                        .beginFill('white')\n",
        "                        .fillRect(x, y, width, height)\n",
        "                        .endFill();\n",
        "                }\n",
        "            },\n",
        "        }\n",
        "    });\n",
        "\n",
        "    // Update icon when clicking on a brush type.\n",
        "    document.getElementById('file_path_selects').addEventListener('change', onChangeImage);\n",
        "\n",
        "    function onChangeImage(evt) {\n",
        "        var img = new Image();\n",
        "        img.src = document.getElementById('file_path_selects').value;\n",
        "        google.colab.kernel.invokeFunction('notebook.init_image', [document.getElementById('file_path_selects').value], {});\n",
        "        img.onload = () => {\n",
        "            document.getElementById('canvas-wrap').width = img.naturalWidth;\n",
        "            document.getElementById('canvas-wrap').height = img.naturalHeight;\n",
        "\n",
        "            // canvasエリアの高さ・幅を設定\n",
        "            imageCanvas.width = img.naturalWidth;\n",
        "            imageCanvas.height = img.naturalHeight;\n",
        "\n",
        "            // 画像を設定\n",
        "            imageCtx.drawImage(img, 0, 0, imageCanvas.width, imageCanvas.height);\n",
        "\n",
        "            drawCanvas.width = img.naturalWidth;\n",
        "            drawCanvas.height = img.naturalHeight;\n",
        "\n",
        "            // 描画領域をクリア\n",
        "            sketcher.memento.restore();\n",
        "        };\n",
        "    }\n",
        "\n",
        "    function getBrushType() {\n",
        "        var brushType = document.querySelector('input[name=\"brush\"][type=radio]:checked').id.split('_')[1].toLowerCase();\n",
        "        return brushType;\n",
        "    }\n",
        "\n",
        "    document.getElementById('clear').addEventListener('click', function (evt) {\n",
        "        evt.preventDefault();\n",
        "        sketcher.clear();\n",
        "    });\n",
        "\n",
        "    document.getElementById('undo').addEventListener('click', function (evt) {\n",
        "        evt.preventDefault();\n",
        "        sketcher.memento.undo();\n",
        "    });\n",
        "\n",
        "    document.getElementById('redo').addEventListener('click', function (evt) {\n",
        "        evt.preventDefault();\n",
        "        sketcher.memento.redo();\n",
        "    });\n",
        "\n",
        "    document.getElementById('save').addEventListener('click', function (evt) {\n",
        "        google.colab.kernel.invokeFunction('notebook.mask_image', [drawCanvas.toDataURL('image/jpeg')], {});\n",
        "    });\n",
        "\n",
        "    onChangeImage();\n",
        "</script>\n",
        "'''\n",
        "\n",
        "display(HTML(canvas_html % (sketchable_script, options_html)))\n",
        "\n",
        "print(\"■■■■■■■■■■■■■■■\")\n",
        "print(\"■　元画像に対して修正を依頼したい箇所をペンや四角形で塗りつぶしてください。\")\n",
        "print(\"■　「保存」ボタンをクリックした後、次の【⑤-B】のセルを実行してください。\")\n",
        "print(\"■■■■■■■■■■■■■■■\")\n",
        "\n",
        "# セル終了\n",
        "finish_cell(ckey)"
      ],
      "metadata": {
        "cellView": "form",
        "id": "CTSGT1tB9DvF"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## ⑤-B　inpaintingの実行"
      ],
      "metadata": {
        "id": "WDnMaHsbQ0xV"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "#@markdown ■■■■■■■■■■■■■■■■■■\n",
        "\n",
        "#@markdown 呪文や変化度などを入力してから、セルを実行してください\n",
        "\n",
        "#@markdown ■■■■■■■■■■■■■■■■■■\n",
        "\n",
        "#@markdown ### 呪文\n",
        "\n",
        "#@markdown マスクをかけた領域の画像をどのように描画するか、日本語もしくは英語の呪文（文章・単語・フレーズ）を入力してください\n",
        "p_prompt = \"\" #@param {type:\"string\"}\n",
        "\n",
        "#@markdown ### 変化度\n",
        "\n",
        "#@markdown 元画像からの変化度を指定してください。変化度=0の場合、変化度を徐々に大きくさせて生成します。\n",
        "\n",
        "#@markdown ～0.2: ほとんど変化しない, ～0.3: パーツが若干変化する, ～0.6: 元絵の雰囲気を残しつつ絵柄が変わる, ～0.8: 元絵の雰囲気があまり残らなくなる, 0.9: 元絵をほぼ無視する\n",
        "\n",
        "p_strength = 0.5 #@param {type:\"slider\", min:0, max:1, step: 0.01}\n",
        "\n",
        "#@markdown ### 画像生成枚数\n",
        "\n",
        "#@markdown （値が大きいほど沢山の画像が生成されます）\n",
        "p_image_nums = 3 #@param {type:\"slider\", min:1, max:20}\n",
        "\n",
        "#@markdown ### 推定パラメーター\n",
        "\n",
        "#@markdown 試行回数\n",
        "p_num_inference_steps = 100 #@param {type:\"slider\", min:1, max:200}\n",
        "\n",
        "#@markdown 試行精度\n",
        "p_guidance_scale = 15.0 #@param {type:\"slider\", min:0.1, max:30, step: 0.1}\n",
        "\n",
        "#@markdown シード値（0の場合はランダムシードを生成します）\n",
        "p_manual_seed = 0 #@param {type:\"slider\", min:0, max:1024, step: 1}\n",
        "\n",
        "\n",
        "# セル実行可否\n",
        "ckey = \"⑤-B\"\n",
        "check_exec_dict(ckey)\n",
        "\n",
        "seed = p_manual_seed if p_manual_seed else random.randint(1, 1024)\n",
        "\n",
        "# ---------\n",
        "from PIL import Image\n",
        "from IPython.display import HTML\n",
        "from base64 import b64encode\n",
        "\n",
        "pil_img = Image.open('/content/mask.jpg')\n",
        "\n",
        "w = pil_img.size[0]\n",
        "h = pil_img.size[1]\n",
        "if w % 64 or h % 64:\n",
        "    print(\"■■■■■■■■■■■■■■■\")\n",
        "    print(\"■　** ERROR **\")\n",
        "    print(\"■　画像サイズの縦横が64で割り切れない値になっています。\")\n",
        "    print(\"■　画像サイズを見直して下さい。\")\n",
        "    print(f\"■　画像横幅: {w}({w / 64:.3f}), 画像縦幅: {h}({h / 64:.3f})\")\n",
        "    print(\"■■■■■■■■■■■■■■■\")\n",
        "    raise IpyExit\n",
        "\n",
        "if not p_prompt:\n",
        "    print(\"■■■■■■■■■■■■■■■\")\n",
        "    print(\"■　** ERROR **\")\n",
        "    print(\"■　修正箇所の呪文が入力されていません。\")\n",
        "    print(\"■■■■■■■■■■■■■■■\")\n",
        "    raise IpyExit\n",
        "\n",
        "file_paths = execute(\n",
        "    translate_jp2en(p_prompt),\n",
        "    p_prompt,\n",
        "    False,\n",
        "    seed,\n",
        "    \"\",\n",
        "    w,\n",
        "    h,\n",
        "    p_image_nums,\n",
        "    p_num_inference_steps,\n",
        "    p_guidance_scale,\n",
        "    inpainting=True,\n",
        "    init_image_path='/content/init.png',\n",
        "    mask_image_path='/content/mask.jpg',\n",
        "    strength=p_strength,\n",
        "    saving=False,\n",
        ")\n"
      ],
      "metadata": {
        "id": "Z6IRB1XabbNq",
        "cellView": "form"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "# ⑥　FILM 実行\n",
        "\n",
        "（2つの画像を補間した映像を生成する）\n"
      ],
      "metadata": {
        "id": "SZ7aBEdEzy4i"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "## ⑥-A　補間映像用UIの表示"
      ],
      "metadata": {
        "id": "Tqd4b_5Uaivf"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "#@markdown ■■■■■■■■■■■■■■■■■■\n",
        "\n",
        "#@markdown 下の【⑥-A】を実行すると、補間映像指定用UIが出力されます。\n",
        "\n",
        "#@markdown ③で出力した画像を想定していますが、 `outputs` の中にアップロードされた png画像 であれば生成対象とすることができます。\n",
        "\n",
        "#@markdown ※両方とも同じ画像サイズに揃えて下さい。\n",
        "\n",
        "#@markdown ■■■■■■■■■■■■■■■■■■\n",
        "\n",
        "from ipywidgets import Label, Layout, GridBox, interact\n",
        "from IPython.display import display\n",
        "from glob import glob\n",
        "from PIL import Image\n",
        "\n",
        "animation_widgets = []\n",
        "\n",
        "images = list(sorted([n for n in glob(\"/content/outputs/**/*.png\", recursive=True)]))\n",
        "\n",
        "%cd /content/\n",
        "\n",
        "# セル実行可否\n",
        "ckey = \"⑥-A\"\n",
        "check_exec_dict(ckey)\n",
        "\n",
        "# ----------------------------------\n",
        "\n",
        "# 学習済みパラメータ・ダウンロード\n",
        "\n",
        "%cd frame_interpolation\n",
        "! pip install --upgrade gdown\n",
        "\n",
        "import gdown\n",
        "gdown.download('https://drive.google.com/uc?id=1rEABCoyQFkmHGieKDhHXW2ZYJi12lofI', 'pretrained_models.zip', quiet=False)\n",
        "! unzip pretrained_models.zip\n",
        "\n",
        "%cd ~\n",
        "\n",
        "# ---------\n",
        "\n",
        "print(\"-------------------------------------\")\n",
        "print(f\"■ 始点画像\")\n",
        "\n",
        "def load_start_img(img_path: str):\n",
        "  img_bytes = None\n",
        "  with open(img_path, \"rb\") as f:\n",
        "    img_bytes = f.read() \n",
        "  pil_img = Image.open(img_path)\n",
        "\n",
        "  img = widgets.Image(\n",
        "      value=img_bytes,\n",
        "      format='png',\n",
        "      width=int(pil_img.size[0] / 3),\n",
        "      height=int(pil_img.size[1] / 3),\n",
        "  )\n",
        "\n",
        "  return img\n",
        "\n",
        "start_image_dd = widgets.Dropdown(\n",
        "    options=images,\n",
        "    value=images[0],\n",
        "    disabled=False,\n",
        "    layout=widgets.Layout(width='600px', height='auto')\n",
        ")\n",
        "interact(load_start_img, img_path=start_image_dd)\n",
        "\n",
        "# ---------\n",
        "\n",
        "print(\"-------------------------------------\")\n",
        "print(f\"■ 終点画像\")\n",
        "\n",
        "def load_end_img(img_path: str):\n",
        "  img_bytes = None\n",
        "  with open(img_path, \"rb\") as f:\n",
        "    img_bytes = f.read() \n",
        "  pil_img = Image.open(img_path)\n",
        "\n",
        "  img = widgets.Image(\n",
        "      value=img_bytes,\n",
        "      format='png',\n",
        "      width=int(pil_img.size[0] / 3),\n",
        "      height=int(pil_img.size[1] / 3),\n",
        "  )\n",
        "\n",
        "  return img\n",
        "\n",
        "end_image_dd = widgets.Dropdown(\n",
        "    options=images,\n",
        "    value=images[-1],\n",
        "    disabled=False,\n",
        "    layout=widgets.Layout(width='600px', height='auto')\n",
        ")\n",
        "interact(load_end_img, img_path=end_image_dd)\n",
        "\n",
        "display(GridBox(animation_widgets, layout=widgets.Layout(grid_template_columns=\"repeat(3, 20%)\")))\n",
        "finish_cell(ckey)\n",
        "\n",
        "print(\"■■■■■■■■■■■■■■■\")\n",
        "print(\"■　開始画像と終了画像を選んだ後、【⑥-B】のセルを実行して下さい。\")\n",
        "print(\"■■■■■■■■■■■■■■■\")\n"
      ],
      "metadata": {
        "id": "LONasWJUzEOb",
        "cellView": "form"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## ⑥-B　補間映像の生成"
      ],
      "metadata": {
        "id": "iN9g2pH-arq-"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "#@markdown ■■■■■■■■■■■■■■■■■■\n",
        "\n",
        "#@markdown 【⑥-A】で始点画像と終点画像を選んでこのセルを実行すると、その間がスムーズに動くように補間映像を作成します。\n",
        "\n",
        "#@markdown ■■■■■■■■■■■■■■■■■■\n",
        "\n",
        "# セル実行可否\n",
        "ckey = \"⑥-B\"\n",
        "check_exec_dict(ckey)\n",
        "\n",
        "import os, shutil\n",
        "import subprocess\n",
        "from datetime import datetime\n",
        "\n",
        "start_path = start_image_dd.options[start_image_dd.index]\n",
        "end_path = end_image_dd.options[end_image_dd.index]\n",
        "print(f\"start: {start_path}\")\n",
        "print(f\"end: {end_path}\")\n",
        "\n",
        "# 指示日時\n",
        "film_dir = f'/content/film/{datetime.now():%Y%m%d_%H%M%S}'\n",
        "os.makedirs(film_dir, exist_ok=True)\n",
        "\n",
        "# フォルダ内にコピー\n",
        "shutil.copy(start_path, os.path.join(film_dir, \"01.png\"))\n",
        "\n",
        "# フォルダ内にコピー\n",
        "shutil.copy(end_path, os.path.join(film_dir, \"02.png\"))\n",
        "\n",
        "try:\n",
        "    !python -m frame_interpolation.eval.interpolator_cli --pattern $film_dir \\\n",
        "    --model_path \"/content/frame_interpolation/pretrained_models/film_net/Style/saved_model\" \\\n",
        "    --times_to_interpolate 6 --output_video\n",
        "\n",
        "    # セル終了\n",
        "    finish_cell(ckey)\n",
        "except Exception as e:\n",
        "    print(e)\n",
        "    print(traceback.format_exc())\n",
        "    print(\"■■■■■■■■■■■■■■■\")\n",
        "    print(\"■　** ERROR **\")\n",
        "    print(\"■　FILMコマンドの実行に失敗しました。\")\n",
        "    print(\"■■■■■■■■■■■■■■■\")\n",
        "    raise IpyExit\n"
      ],
      "metadata": {
        "cellView": "form",
        "id": "OpZIiY5i6oLN"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "# 起こる可能性のあるエラー"
      ],
      "metadata": {
        "id": "14d4al9KasrD"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "## GPU上限突破"
      ],
      "metadata": {
        "id": "0s_oDN64aDNd"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "下記のようなエラーが出た場合、GPUの使用量上限に達しました。\n",
        "\n",
        "無料域の場合、24時間以上待っていただくと再度利用できるようになります。\n",
        "\n",
        "![上限](https://drive.google.com/uc?export=view&id=1xxg5yM-wgNkAr1FboAM8DS2K-aqmtw5d)\n"
      ],
      "metadata": {
        "id": "4KKxS-dEaB1q"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "## 環境のクラッシュ"
      ],
      "metadata": {
        "id": "CIJc-81waK1K"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "下記のようなエラーが出た場合、環境がクラッシュしました。\n",
        "\n",
        "セッションを一度削除して、環境を作り直してください\n",
        "\n",
        "![クラッシュ](https://drive.google.com/uc?export=view&id=1qMoxfUggI_jDjrDifj7UgOrSmsEOMmHY)\n",
        "\n",
        "![クラッシュ](https://drive.google.com/uc?export=view&id=1WFHmCzEkCLi8XnHyl7goicNQPL67RHhX)\n",
        "\n",
        "![削除](https://drive.google.com/uc?export=view&id=12UaonO4UvI_HCnJI95od15_yagVIVsRD)\n",
        "\n",
        "![削除](https://drive.google.com/uc?export=view&id=1smRW97KjP8fqSy3E5dtfWJHpyBtEMKca)\n"
      ],
      "metadata": {
        "id": "_nyb23uCaJDr"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "## 環境の自動削除"
      ],
      "metadata": {
        "id": "DrRgrOxTaQie"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "下記のようなエラーが出た場合、時間経過や上記クラッシュなどによりColabから環境が既に破棄されています。\n",
        "\n",
        "セッションを一度削除して、環境を作り直してください\n",
        "\n",
        "![Googleドライブ連携](https://drive.google.com/uc?export=view&id=1Oa5SNwStqzR6qVMEzxg8PdLmO3FTpr1l)\n"
      ],
      "metadata": {
        "id": "JGMwQEbfaPWt"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "## GPU未使用警告"
      ],
      "metadata": {
        "id": "zKqbxWfJaWN4"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "作業中に下記のような警告が出ることがありますが、そのまま進めていただいて問題ありません\n",
        "\n",
        "　（GPUを使うのはAIを実行する箇所だけですので、その他のセルを作業中に出る可能性があります。）\n",
        "\n",
        "![警告](https://drive.google.com/uc?export=view&id=1mRW32urnPQ4LS4xrLEoPdp_XCqlq1HUF)\n"
      ],
      "metadata": {
        "id": "hKaZ45HjaUlX"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "## 学習モデルを変えたい場合"
      ],
      "metadata": {
        "id": "B7mERyOKjVQ5"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "お手数ですが、セッションを一度削除して、環境を作り直してください\n",
        "\n",
        "![削除](https://drive.google.com/uc?export=view&id=12UaonO4UvI_HCnJI95od15_yagVIVsRD)\n",
        "\n",
        "![削除](https://drive.google.com/uc?export=view&id=1smRW97KjP8fqSy3E5dtfWJHpyBtEMKca)\n"
      ],
      "metadata": {
        "id": "yUNa57uEjcK0"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "# 利用規約\n",
        "\n",
        " 1. 利用上の制限は [Stable Diffusion](https://github.com/huggingface/diffusers/blob/main/LICENSE) に準じます\n",
        " 2. Twitterなどに投稿された時に `#StableDiffusionGenerator` もしくは `#SDGr` タグを付けていただけると見つけやすくて嬉しいです\n"
      ],
      "metadata": {
        "id": "Db32xQjUmd5M"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "\n",
        "# クレジット\n",
        "\n",
        " ツール名: StableDiffusionGenerator, SDGr\n",
        "\n",
        " 作者: miu [@miu200521358](https://twitter.com/miu200521358)"
      ],
      "metadata": {
        "id": "Cbpw1jGtoDPN"
      }
    }
  ]
}